-
Notifications
You must be signed in to change notification settings - Fork 2
Workflow reference
Ten workflows, all workflow_dispatch only — nothing runs on push or pull request. Building
WebRTC costs about an hour of runner time, so it happens when asked and not otherwise.
| Workflow | Runner | Output | Consumed by |
|---|---|---|---|
WebRtcNativeWindowsStaticLib |
windows-latest |
webrtc.lib |
native C/C++ linking |
WebRtcNativeWindowsDynamicLib |
windows-latest |
webrtc.dll (+ import lib, PDB) |
WebRTCme.Bindings.Native |
WebRtcNativeLinuxStaticLib |
ubuntu-latest |
libwebrtc.a |
native C/C++ linking |
WebRtcNativeLinuxSharedLib |
ubuntu-latest |
libwebrtc.so |
WebRTCme.Bindings.Native |
WebRtcNativeMacOsStaticLib |
macos-latest |
libwebrtc.a |
not consumed |
WebRtcNativeMacOsSharedLib |
macos-latest |
libwebrtc.dylib |
not consumed |
WebRtcNativeAndroidLib |
ubuntu-latest |
libwebrtc.aar |
WebRTCme.Bindings.Maui.Android |
WebRtcNativeIosLib |
macos-latest |
WebRTC.xcframework (iOS slices) |
WebRTCme.Bindings.Maui.iOS |
WebRtcNativeMacCatalystLib |
macos-latest |
WebRTC.xcframework (Catalyst slices) |
WebRTCme.Bindings.Maui.MacCatalyst |
WebRtcNativeInteropWindows |
windows-latest |
WebRtcInterop.dll |
the future Windows binding |
The six desktop workflows are the same job with three variables: operating system, whether the shared-library patch is applied, and which file is collected.
-
Checkout this repository (
actions/checkout@v4) — needed for the composite action. - Resolve WebRTC branch — see Branch selection. Fails fast on a bad value.
- Free disk space (Linux only) — remove preinstalled toolchains; a WebRTC checkout does not fit alongside them.
-
Install depot_tools and put it first on
PATH, so its bundled Python and Git win over the runner's. -
Bootstrap gclient — a bare
gclientcall, which downloads its own dependencies on first run. -
Configure Git —
gclientrefuses to run without an identity. It is a bot identity, not a commit author for this repository. -
Fetch WebRTC source —
fetch --nohooks, then fetch the resolved branch-head explicitly and check it out, thengclient sync -D --force --reset. - Patch for a shared library — dynamic/shared workflows only.
-
Build —
gn genthenautoninja -C out/Default webrtc. - Collect — locate the output by name and fail loudly if it is missing.
-
Upload (
actions/upload-artifact@v4) withif-no-files-found: error.
git checkout branch-heads/NNNN depends on the remote refspec already covering branch-heads. The
workflows do not rely on that:
git fetch origin "+refs/branch-heads/${BRANCH}:refs/remotes/branch-heads/${BRANCH}"
git checkout -B "webrtc-${BRANCH}" "refs/remotes/branch-heads/${BRANCH}"autoninja -C out/Default with no target builds the entire tree, including tests. The workflows
build webrtc specifically and pass rtc_include_tests=false rtc_build_tools=false rtc_build_examples=false, none of which affect the contents of the library.
Everything lives on C:. The hosted runner's D: has roughly 14 GB free, nowhere near enough.
DEPOT_TOOLS_WIN_TOOLCHAIN: 0 builds with the Visual Studio installed on the runner instead of
Google's internal toolchain, which is not publicly accessible. The install path is discovered with
vswhere and exported as GYP_MSVS_OVERRIDE_PATH plus vs<year>_install, rather than being
hard-coded to an edition that may change with the runner image. The year is Chromium's name for the
release, not the version number: the runners now ship Visual Studio 18, which vs_toolchain.py
calls 2026 and looks up via vs2026_install.
depot_tools is downloaded as a zip and extracted with 7z, not cloned — a plain clone on Windows
misses the bootstrap step.
The dynamic workflow also collects webrtc.dll.lib (the import library, needed to link from C/C++)
and webrtc.dll.pdb (symbols, which make crash dumps from P/Invoke callers readable).
The Free disk space step removes /usr/share/dotnet, /usr/local/lib/android, /opt/ghc,
/usr/local/share/boost, /usr/local/.ghcup, /usr/share/swift and $AGENT_TOOLSDIRECTORY, then
runs apt-get clean. Disk usage is printed before and after.
build/install-build-deps arrives with the gclient-pulled build/ directory and has changed name
between a shell script and a Python script across branches, so the workflows probe for either.
The macOS artifacts have no consumer today — WebRTCme targets Mac Catalyst, which is served by the iOS xcframework. These workflows are kept as a check that the tree still builds on Apple silicon; see Platform layers.
macos-latest is Apple silicon, so target_cpu defaults to arm64; choose x64 for Intel Macs.
The collect step runs lipo -info so the log records what was actually produced.
macOS ships BSD sed, where -i requires an explicit backup suffix. Every in-place edit is
sed -i '' …. Dropping the empty '' makes sed treat the next argument as the suffix and the
edit silently goes to the wrong place — this is the single easiest way to break the macOS shared
build.
Fetches webrtc_android rather than webrtc; that solution pulls the Android SDK and NDK that
build_aar.py needs.
No shared-library patch is involved — build_aar.py already produces an .aar containing JNI
shared objects plus the Java API. It does need a patch of its own, though.
build_aar.py takes classes.jar straight from the dist_jar("libwebrtc") target in
sdk/android/BUILD.gn. That target sets direct_deps_only = true, so only the jars of the targets
it lists itself are merged, not their dependencies.
Since WebRTC moved to jni_zero, the JNI glue no longer lives in the Java libraries. It is
generated into separate generated_*_jni_java targets which those libraries depend on and which
the dist_jar does not list. The generated classes therefore never reach classes.jar, and an app
using the AAR dies on the first line it runs:
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/webrtc/PeerConnectionFactoryJni;
at org.webrtc.PeerConnectionFactory.initialize(PeerConnectionFactory.java:328)
The symptom is worth recognising because nothing upstream of it complains: the build succeeds, the AAR packages, the artifact uploads, and the binding compiles against it. The classes are simply not there. It is visible in the archive itself —
unzip -p libwebrtc.aar classes.jar | jar -t | grep Jni.class # empty on an unpatched build
tools/add_generated_jni_to_aar.py adds the eighteen non-test generators to the dist_jar, and
the collect step then refuses to publish an archive that still lacks PeerConnectionFactoryJni.
The script asserts its anchors and is idempotent; if an assertion fires, update it rather than
skipping the step, the same rule the shared-library patch follows.
M152 (branch-heads/7977) is the first branch this repository built where it bites.
The arch input is space-separated ABI names. Leaving it empty builds build_aar.py's own
defaults: armeabi-v7a, arm64-v8a, x86, x86_64. The collect step lists the .so files
inside the archive so the log shows which ABIs really shipped.
This is the workflow most likely to run out of disk, which is why the space-clearing step matters most here.
Two workflows, one toolchain. Both fetch webrtc_ios and run
tools_webrtc/ios/build_ios_libs.py; only the arch list differs, because Mac Catalyst is an iOS
app running on a Mac rather than a separate platform.
| Workflow | Default arch
|
|---|---|
WebRtcNativeIosLib |
device:arm64 simulator:arm64 simulator:x64 |
WebRtcNativeMacCatalystLib |
catalyst:arm64 catalyst:x64 |
They are split to match the split on the bindings side, where iOS and Mac Catalyst are separate
projects. The cost is a second macOS run — the priciest runner, and roughly 40 minutes of each run
is gclient sync over an identical tree.
Run them against the same branch. Two independently dispatched workflows can resolve to different milestones if a rollover happens between them. When refreshing Apple support, pin
webrtc_branchexplicitly on both rather than leaving it blank.
build_ios_libs.py accepts these values (ENABLED_ARCHS):
| Value | Slice |
|---|---|
device:arm64 |
iOS device |
simulator:arm64 |
iOS simulator on Apple silicon |
simulator:x64 |
iOS simulator on Intel |
catalyst:arm64 |
Mac Catalyst on Apple silicon |
catalyst:x64 |
Mac Catalyst on Intel |
arm64, x64
|
legacy aliases, device only |
Upstream's own default omits the catalyst slices entirely, which is why
WebRtcNativeMacCatalystLib names them explicitly.
An earlier version of the iOS workflow passed
--arch arm64 x64. Those legacy aliases mean device only, so the resulting xcframework had no simulator and no Catalyst slice.
The framework is zipped before upload. upload-artifact does not preserve symlinks, and an
xcframework that loses its symlinks will not link. The zip is created from inside out_ios_libs
so WebRTC.xcframework sits at the archive root, and uploaded with compression-level: 0 because
it is already compressed.
A release build carries no debug information, so a crash inside the shipped framework symbolicates
to nothing: nm finds thirteen text symbols, all RTCLog*, and a faulting stack is eight bare
addresses that atos cannot resolve. That is fine until something crashes on a libwebrtc-internal
thread, where the addresses are the only evidence there is — WebRTCme spent a night in September
2026 bisecting an Apple crash that a symbol table would have named.
Setting dsyms: true adds --extra-gn-args enable_dsyms=true symbol_level=2 and uploads the
bundles as a second artifact, webrtc-<platform>-dsyms-m<milestone>-<branch>.
Prefer this over
build_config: debugfor diagnosing a crash.enable_dsymsputs the debug information in a separate bundle and leaves the linked binary alone, so the framework is still the framework that ships. A debug build changes both the code and its timing, and a racy fault is exactly what timing perturbs — it may stop reproducing.
The flag is off by default, so an ordinary refresh produces the same artifact it always did. Note
that --extra-gn-args is declared nargs='*' upstream: it takes both values at once and must come
last, because a second occurrence replaces the first rather than adding to it.
WebRtcNativeInteropWindows is the only workflow that builds code from this repository.
Everything else compiles Google's tree unmodified apart from the patch.
It adds two steps to the Windows shape:
-
Graft WebRtcInterop into the checkout — copies
WebRtcInterop/tosrc/WebRtcInterop, because itsBUILD.gnimports../webrtc.gniand only resolves from inside the tree. It fails early ifsrc/Interop.ccis empty, so a stripped copy cannot silently produce nothing. -
A sixth patch edit — appends
"//WebRtcInterop"to the rootgroup("default")deps soninjareaches the grafted target. Asserted before and after, like the other five.
It then builds the WebRtcInterop target rather than webrtc, and collects every DLL beside it:
a component build splits across many, and the shim needs all of them at run time.
The shim itself is unfinished — see Repository layout for what
Interop.cc does and does not do yet. The workflow exists so that work is buildable again.
| Action | Version |
|---|---|
actions/checkout |
v4 |
actions/upload-artifact |
v4 |
microsoft/setup-msbuild |
v2 |
actions/upload-artifact@v3 was retired by GitHub; workflows still using it fail immediately.
Note that v4 artifacts are immutable and same-named uploads no longer merge — each workflow here
uploads exactly one artifact per run, so neither matters in practice.
Using it
Building on it
Reference
When it breaks