-
Notifications
You must be signed in to change notification settings - Fork 2
Shared library patch
WebRTC's build system produces static libraries only. That is fine for a C++ application, and
useless for P/Invoke from C#, which needs a .dll, .so or .dylib exporting symbols.
Three workflows — WindowsDynamicLib, LinuxSharedLib, MacOsSharedLib — patch the checkout
in place before generating build files. This is the part of the repository with the least margin
for error, so it is documented in full.
All are applied to the WebRTC checkout at src/, after the branch is checked out and synced, and
before gn gen.
BUILD.gn: rtc_static_library → rtc_shared_library
rtc_static_library is WebRTC's own GN template. Swapping it for rtc_shared_library changes what
the top-level targets produce.
BUILD.gn: delete every line containing 'complete_static_lib'
complete_static_lib tells the archiver to fold all dependencies into one .a. It is meaningless
for a shared library, and GN rejects it on a shared_library target.
webrtc.gni: !build_with_chromium && is_component_build → false
Line 16 of webrtc.gni guards a print and an assert(!is_component_build, "Component builds are not supported in WebRTC."). Forcing the condition to false disables that refusal.
Worth being clear about what this means: component builds are genuinely unsupported upstream, and edit 5 exists because of a real consequence of overriding that. Treat the whole patch as living outside what WebRTC tests.
rtc_tools/BUILD.gn: delete every line containing ':frame_analyzer'
frame_analyzer does not link under a component build. It is a developer tool, absent from the
shipped library, so removing the dependency costs nothing.
webrtc.gni: if (build_with_chromium && defined(deps)) -> if (defined(deps))
Five templates in webrtc.gni contain a block that rewrites any dependency on
//third_party/abseil-cpp/* into a single dependency on //third_party/abseil-cpp:absl. WebRTC
only applies it when built inside Chromium.
Standalone, WebRTC therefore depends on fine-grained Abseil targets such as
//third_party/abseil-cpp/absl/container:raw_hash_set, which are source_sets and link directly
into whatever depends on them. Under a component build //third_party/abseil-cpp:absl is also
built as its own shared library. The final link then sees the same symbol twice:
lld-link: error: duplicate symbol: absl::container_internal::kDefaultIterControl
>>> defined at obj/third_party/abseil-cpp/absl/container/raw_hash_set/raw_hash_set.obj
>>> defined at third_party_abseil-cpp_absl.dll.lib(third_party_abseil-cpp_absl.dll)
Dropping the build_with_chromium guard makes the standalone build route Abseil the same way
Chromium does, so there is exactly one copy.
This surfaces at the very end of the build — 3429 of 3430 targets complete — because it is a link error in the final artifact.
gn gen out/Default --args="is_debug=false target_os=\"…\" target_cpu=\"…\"
is_component_build=true
rtc_enable_symbol_export=true
rtc_include_tests=false
rtc_build_tools=false
rtc_build_examples=false"
is_component_build=true produces the shared library. rtc_enable_symbol_export=true is what puts
symbols in its export table — without it the library exists but exports nothing usable.
The edits are identical; only the tool differs.
Linux — GNU sed:
sed -i 's/rtc_static_library/rtc_shared_library/g' BUILD.gn
sed -i '/complete_static_lib/d' BUILD.gn
sed -i 's/!build_with_chromium && is_component_build/false/g' webrtc.gni
sed -i '/:frame_analyzer/d' rtc_tools/BUILD.gn
sed -i 's/if (build_with_chromium && defined(deps))/if (defined(deps))/g' webrtc.gnimacOS — BSD sed, where -i requires a backup suffix:
sed -i '' 's/rtc_static_library/rtc_shared_library/g' BUILD.gn
sed -i '' '/complete_static_lib/d' BUILD.gn
sed -i '' 's/!build_with_chromium && is_component_build/false/g' webrtc.gni
sed -i '' '/:frame_analyzer/d' rtc_tools/BUILD.gn
sed -i '' 's/if (build_with_chromium && defined(deps))/if (defined(deps))/g' webrtc.gniThe empty
''is not optional. Without it BSD sed takes the next argument as the backup suffix, and the edit lands somewhere unintended. This is the classic way to break the macOS shared build, and it fails quietly.
Windows — PowerShell:
(Get-Content BUILD.gn).replace('rtc_static_library', 'rtc_shared_library') | Set-Content BUILD.gn
(Get-Content BUILD.gn) -notmatch 'complete_static_lib' | Set-Content BUILD.gn
(Get-Content webrtc.gni).replace('!build_with_chromium && is_component_build', 'false') | Set-Content webrtc.gni
(Get-Content rtc_tools\BUILD.gn) -notmatch ':frame_analyzer' | Set-Content rtc_tools\BUILD.gn
(Get-Content webrtc.gni).replace('if (build_with_chromium && defined(deps))', 'if (defined(deps))') | Set-Content webrtc.gni.replace() is the .NET string method — ordinal, not a regex, so ! and && need no escaping.
-notmatch filters the array of lines, which is the PowerShell equivalent of sed '/…/d'.
A find-and-replace that finds nothing does not fail: sed exits 0, and the build carries on to
produce a static library that will not load from C#. That failure would surface an hour later, or
worse, at run time in an application.
So each workflow asserts every anchor exists before editing:
assert_contains BUILD.gn 'rtc_static_library'
assert_contains BUILD.gn 'complete_static_lib'
assert_contains webrtc.gni '!build_with_chromium && is_component_build'
assert_contains webrtc.gni 'if (build_with_chromium && defined(deps))'
assert_contains rtc_tools/BUILD.gn ':frame_analyzer'A missing anchor stops the run immediately with a message saying the patch needs updating for that WebRTC branch. That is the intended signal when upstream refactors: fix the patch, do not remove the assertion.
is_component_build=true means what it says: the tree is split across separate shared libraries,
so the output directory holds absl.dll, boringssl.dll and others beside webrtc.dll.
webrtc.dll alone will not load.
The collect steps therefore gather every .dll / .so / .dylib next to the main library,
not just the one. Anything consuming these needs the whole set on its library search path.
Linux
file libwebrtc.so # expect: ELF shared object
nm -D --defined-only libwebrtc.so | wc -l # expect: many, not zeromacOS
file libwebrtc.dylib # expect: Mach-O dynamically linked shared library
lipo -info libwebrtc.dylib
nm -gU libwebrtc.dylib | headWindows
dumpbin /exports webrtc.dll | Select-Object -First 40An empty export table means edit 3 or rtc_enable_symbol_export did not take effect.
The patch is deliberately narrow — five anchors — so a break is easy to diagnose. Verify against a new branch before assuming:
B=refs/branch-heads/7977
curl -s "https://webrtc.googlesource.com/src/+/$B/webrtc.gni?format=TEXT" | base64 -d \
| grep -n 'build_with_chromium && is_component_build'
curl -s "https://webrtc.googlesource.com/src/+/$B/BUILD.gn?format=TEXT" | base64 -d \
| grep -c 'rtc_static_library'All five anchors are present in branch-heads/7977 (Chromium M152).
The technique comes from webrtc-sdk/libwebrtc; see
WebRtcInterop/NOTICE.
Using it
Building on it
Reference
When it breaks