Build¶
The plug-in does not build standalone¶
It must sit at obs-studio/plugins/obs-streamelements-core, with the sibling
obs-studio/plugins/obs-browser present — SEBrowserWidget.cpp includes
../../obs-browser/panel/browser-panel.hpp by relative path. Configure and
build from the obs-studio root, not from this directory.
On Windows the preset is windows-x64-streamelements, defined in
CMakeUserPresets.json here (CI copies it to the obs-studio root and deletes
it from here).
Registering the plug-in in an OBS tree is version-dependent¶
This is the single most common build failure, and it is in the host tree, not in this repository.
OBS 33.0.0 renamed add_obs_plugin to add_core_module, and replaced the
removed obs_add_core_module() runtime call with a list baked at configure
time by set_obs_core_modules(), which generates libobs/obs-core-modules.c
— and libobs links it.
So anything that replaces plugins/CMakeLists.txt — as the Windows CI job
does, to build only libobs and this plug-in — must write both lines, or
libobs fails on a missing source file, with an error that has nothing to do
with this plug-in. Appending to upstream's file instead (what the macOS job
does) needs only the rename. set_obs_core_modules() also appears twice in
upstream's file; the other is the early return under
if(NOT ENABLE_CORE_MODULES), so an insertion has to target the last one.
The only obs-provided helpers this repository's CMakeLists.txt calls are
set_target_properties_obs and find_qt; both exist at 32.2.1 and 33.0.1.
Switches¶
| variable | meaning |
|---|---|
STREAMELEMENTS_CRASH_HANDLER |
bugsplat (default), sentry or none. Drives which handler compiles, which SDK links, and the SE_CRASH_HANDLER_* define the factory reads — keep the three together |
STREAMELEMENTS_ENABLE_WYVRN |
the Razer WYVRN integration, ON by default; defines SE_ENABLE_WYVRN |
SE_ENABLE_TESTS |
build the standalone ctest suite, OFF by default |
SE_ENABLE_CENTRAL_WIDGET_DECORATIONS |
0; compiles out the preview decorations and four host API methods. Defined on ISENativeObsUIControlsManager.hpp so all three guarded regions see it |
sentry-native is vendored as deps/sentry-native.zip and extracted by CMake
into the build tree; there is no unzip step to run. If
STREAMELEMENTS_CRASH_HANDLER=sentry and STREAMELEMENTS_SENTRY_DSN is empty,
CMake warns that crash reporting will be inert.
streamelements/Version.generated.hpp must define
STREAMELEMENTS_PLUGIN_VERSION. A placeholder is committed and CI overwrites
it — do not bump it by hand.
Adding files¶
New source files must be added by hand to the explicit
obs-streamelements-core_SOURCES (113 entries) and
obs-streamelements-core_HEADERS (195 entries) lists in CMakeLists.txt.
There is no globbing. New qrc assets go in both
streamelements/streamelements.qrc and the qt6_add_resources(...) FILES
list.
The runtime library, and a block of dead code¶
This project is /MD (MultiThreadedDLL), the same as libobs. Vendored
libraries must match or the link fails with LNK4098, which /WX promotes to
a hard error.
The string(REPLACE "/MD" "/MT" ...) block in CMakeLists.txt is dead
code: obs-studio sets the runtime through the MSVC_RUNTIME_LIBRARY target
property, so the flags never contain a literal /MD and nothing is replaced.
Do not edit¶
- CMake/MSBuild output at the repository root:
qrc_streamelements.cpp,obs-streamelements-core.sln,*.vcxproj*,cmake_install.cmake,CMakeFiles/,RelWithDebInfo/,*.dir/,*_autogen/. Generated, gitignored, and not build definitions — onlyCMakeLists.txtmatters. The equivalent.vcxproj/.slnfiles underdeps/andCI/*/nsis/are vendored source and are tracked. deps/,streamelements/deps/— vendored third-party.data/locale/*.iniother thanen-US.ini— translated externally.
Formatting¶
.clang-format at the root is authoritative: 8-wide tabs
(UseTab: ForContinuationAndIndentation), 80-column limit,
AccessModifierOffset: -8, brace on the next line for function definitions
only (everything else K&R), PointerAlignment: Right,
SortIncludes: false, ReflowComments: false.
Format only the files you edited:
clang-format -i -style=file -fallback-style=none <file>
Never run ./formatcode.sh — it has no exclusions and would reformat all
of deps/ and streamelements/deps/.
Install the pre-commit hook once per clone:
git config core.hooksPath .githooks. It is not installed automatically, and
a clone without it formats nothing.
.githooks/pre-commit formats only the line ranges the commit touches, then
re-stages them. That restriction is the point: the tree was formatted by older
clang-format releases, so a whole-file run with a newer one rewrites code nobody
edited — 19.1.5 reformats 225 lines of tests/test_source_invariants.cpp
unprompted. Line ranges keep the blast radius equal to the diff, which is why
no clang-format version is pinned. A file with unstaged changes is never
re-staged; the hook reports it and fails instead. CLANG_FORMAT=<path>
overrides discovery; SE_SKIP_FORMAT=1 or --no-verify skips it.
CI/check-format.sh exists but no workflow invokes it — formatting is
enforced by the hook, not by the pipeline.
CI¶
| workflow | what it does |
|---|---|
build.yml |
the only build pipeline: Windows (windows-2025-vs2026) and macOS OBS builds, release notes gate, versioning, and on master, tagging and publishing |
tests.yml |
the ctest suite on windows-latest and macos-latest; a red ctest blocks a PR |
pages.yml |
this documentation site — see below |
linear-issue-key.yml |
requires a Linear issue key, and surfaces #partial |
release.yml, upload-obs-symbols.yml, the installer/uninstaller builds, claude*.yml |
release plumbing and review automation |
build.yml pins OBS to a tag in the CHECKOUT env of both the windows
and macos jobs — keep them equal. It currently pins 33.0.1.
It also passes a BUILD_OPTIONS list of -DENABLE_*=OFF flags meant to narrow
an artifact-only build. Do not reuse those locally, and do not trust them
either: ENABLE_UI exists in no OBS tree (checked at 32.2.1 and 33.0.1), so
the frontend is built regardless, and 15 of the 18 flags are reported as "not
used by the project" in the Windows job — the plug-in ones because that job
replaces plugins/CMakeLists.txt, which is what actually narrows the build.
Pre-existing and harmless; fixing it means deciding what CI should build, which
is a bigger change than it looks.
Documentation builds¶
Two MkDocs sites, built by pages.yml from one checkout:
| config | source | published at |
|---|---|---|
mkdocs.yml |
docs/api/ |
/api/ — the JavaScript OBS API reference |
mkdocs.architecture.yml |
docs/architecture/ |
/architecture/ — these pages |
Everything else at the top level of docs/ is copied verbatim to the site
root after both builds and with cp -n, so a docs change can never take the
status page down. Both configs are strict: true with link and anchor
validation on, so a renamed heading or a moved file fails the build instead
of publishing a dead link. Cross-links between the two sites must therefore be
absolute https:// URLs: each build only knows its own docs_dir, and a
relative path into the other site is an unresolvable link.
The toolchain is pinned (mkdocs-material==9.7.7) because an unpinned docs
toolchain is a supply-chain hole that runs on every push to master. To preview
locally:
pip install 'mkdocs-material==9.7.7'
mkdocs build -f mkdocs.yml # /api
mkdocs build -f mkdocs.architecture.yml # /architecture
mkdocs serve -f mkdocs.architecture.yml # live preview
Git conventions¶
- Never commit to
master. Branch and open a PR, even for a small fix. - Put the Linear issue key in the branch name
(
jacob/core-266-track-selive-releases). master is squash-merged, so a branch without a key leaves the work untraceable. - Put
#partialin a PR that does not finish its issue, or Linear closes the issue when the first of several PRs merges. - Conventional Commits:
fix:,feat:, with scopes (fix(ci):). - Never start a commit message with
[WORKFLOW-AUTOMATION]— CI uses that prefix to recognise its own commits and skip the build. RELEASE_NOTES.mdmust be non-empty for a master build to tag a release.
Related¶
- Testing — what the suite is and how to run it.
- Diagnostics — the crash-backend switch in context.