-
-
Notifications
You must be signed in to change notification settings - Fork 234
Expand file tree
/
Copy pathjustfile
More file actions
314 lines (288 loc) · 13.7 KB
/
Copy pathjustfile
File metadata and controls
314 lines (288 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#!/usr/bin/env just --justfile
# OBS Studio plugin for MoQ. See doc/bin/obs.md for the full build guide.
#
# `just obs compile` type-checks the sources on any platform with nothing to
# download. The recipes below build a loadable plugin, which is a bigger ask:
#
# Linux: `nix develop` provides obs-studio/qt6/ffmpeg/cmake, then `just obs build`.
# macOS: native, NOT nix. Needs full Xcode; run outside `nix develop` (its
# toolchain breaks the Xcode build). `just obs setup` downloads
# libobs/Qt6/ffmpeg via buildspec.json (obs-deps).
# Windows: needs Visual Studio 2022; same buildspec download (run from Git Bash).
#
# libmoq is built from the in-tree crate (rs/libmoq) via cargo. MOQ_LOCAL points
# CMake at the repo root by default, so there's no prebuilt release to download.
set quiet
# Plain `cargo` unless set; CI sets it to `mbx`. See rs/justfile for the rule.
_rust_cargo := env_var_or_default("RUST_CARGO", "cargo")
cargo_compile := if _rust_cargo == "" { "cargo" } else { _rust_cargo }
# List all of the available commands.
default:
just --list
# Configure via CMake presets. MOQ_LOCAL defaults to the repo root inside
# CMakeLists.txt (so libmoq builds from rs/libmoq); pass `path=/some/moq` to
# override. We deliberately don't compute the path here: `justfile_directory()`
# resolves to the root justfile when this runs as `just obs setup`, so the
# relative math would point outside the repo.
setup preset="" path="":
#!/usr/bin/env bash
set -euo pipefail
PRESET=$(just preset "{{ preset }}")
if [ -n "{{ path }}" ]; then
echo "Configuring with preset: $PRESET and MOQ_LOCAL={{ path }}"
cmake --preset "$PRESET" -DMOQ_LOCAL="{{ path }}"
else
echo "Configuring with preset: $PRESET (MOQ_LOCAL defaults to the repo root)"
cmake --preset "$PRESET"
fi
# Build via CMake presets. Run `just obs setup` first (it configures + downloads
# deps); not chained here because on macOS setup reconfigures OBS, which is slow.
build preset="":
#!/usr/bin/env bash
set -euo pipefail
PRESET=$(just preset "{{ preset }}")
cmake --build --preset "$PRESET"
# Copy the freshly built plugin into the OBS user plugin dir and launch OBS.
# Uses the installed OBS (no local OBS build required). macOS only for now.
run:
#!/usr/bin/env bash
set -euo pipefail
if [[ "$OSTYPE" != "darwin"* ]]; then
echo "just run is macOS-only for now; copy build_*/obs-moq.* into your OBS plugin dir manually" >&2
exit 1
fi
dest="$HOME/Library/Application Support/obs-studio/plugins"
mkdir -p "$dest"
cp -a build_macos/RelWithDebInfo/obs-moq.plugin "$dest/"
RUST_LOG=debug RUST_BACKTRACE=1 OBS_LOG_LEVEL=debug /Applications/OBS.app/Contents/MacOS/OBS
# Type-check every plugin source, without linking or an obs-deps download.
#
# This is the gate to run in a worktree. `just obs build` needs the
# multi-hundred-MB obs-deps bundle (macOS/Windows) and a full cargo build, per
# worktree; this needs neither, because the dev shell carries all three header
# sets: libobs pinned to the same OBS release buildspec.json downloads (see
# `obs-headers` in flake.nix), Qt6, and ffmpeg. CI links the real thing on Linux
# (.github/workflows/obs.yml).
#
# It compiles the Qt sources too, which the CMake build only does when
# ENABLE_QT and ENABLE_FRONTEND_API are on, so the definitions they gate on are
# set here to match.
compile:
#!/usr/bin/env bash
set -euo pipefail
# Never skips, for the reason `test` doesn't: a compile gate that reports
# success without compiling is worse than no gate. Everything it needs is in
# the dev shell, so the fix is always to enter it.
cxx="${CXX:-c++}"
if ! command -v "$cxx" >/dev/null; then
echo "no C++ compiler at '$cxx'" >&2
exit 1
fi
for pkgs in "Qt6Widgets Qt6Gui Qt6Core" "libavcodec libavutil libswscale libswresample"; do
# shellcheck disable=SC2086
if ! pkg-config --exists $pkgs; then
echo "missing headers for: $pkgs" >&2
echo "run inside 'nix develop', which supplies Qt6 and ffmpeg on every platform" >&2
exit 1
fi
done
# Assign first so a failure in `_includes` still aborts under `set -e`,
# then split on newlines only: an include path may contain spaces.
includes_raw=$(just _includes)
includes=()
while IFS= read -r flag; do includes+=("$flag"); done <<< "$includes_raw"
qt=$(pkg-config --cflags Qt6Widgets Qt6Gui Qt6Core)
ffmpeg=$(pkg-config --cflags libavcodec libavutil libswscale libswresample)
# MOQ_VERSION_STRING only reaches a label in the dock, so any value
# type-checks the same; CMake stamps the real libmoq version.
status=0
for source in src/*.cpp; do
# shellcheck disable=SC2086
# -Wno-unused-command-line-argument: the nix compiler wrapper injects
# link flags, which a syntax-only run reports as unused, once per file.
# $qt and $ffmpeg stay unquoted on purpose: pkg-config hands back one
# space-separated string, and splitting it is the intended reading.
if ! "$cxx" -std=c++17 -fsyntax-only -Wno-unused-command-line-argument \
"${includes[@]}" $qt $ffmpeg \
-DMOQ_FRONTEND_ENABLED -DMOQ_VERSION_STRING='"0.0.0"' "$source"; then
status=1
fi
done
exit $status
# Compile and link the plugin the way CI does, against the dev shell's own
# libobs/Qt6/ffmpeg. Linux-only in practice: it's the one platform where nixpkgs
# has obs-studio, so nothing has to be downloaded. Manual elsewhere, like
# `just rs macos`.
#
# CMAKE_BUILD_TYPE overrides the preset's RelWithDebInfo because the Rust cache
# CI restores holds dev-profile artifacts. A release build of libmoq would
# rebuild its whole dependency tree on every run, and the gate is about whether
# the plugin compiles, which the profile doesn't change.
ci preset="":
#!/usr/bin/env bash
set -euo pipefail
PRESET=$(just preset "{{ preset }}" ci)
cmake --preset "$PRESET" -DCMAKE_BUILD_TYPE=Debug
cmake --build --preset "$PRESET"
# Print the -I flags for libobs and libmoq, shared by `compile` and `test`.
#
# moq.h is generated by cbindgen from rs/libmoq/src, so regenerate rather than
# just locating it: a stale header is how a plugin call to a since-changed
# libmoq function type-checks anyway.
[private]
_includes:
#!/usr/bin/env bash
set -euo pipefail
# The dev shell's pinned headers first, so every platform checks against the
# same libobs. Then the obs-deps framework (macOS/Windows), the OBS sources
# CMake unpacks beside it, and a system install (Linux without nix).
obs_include=""
for candidate in \
"${OBS_INCLUDE_DIR:-}" \
.deps/Frameworks/libobs.framework/Versions/A/Headers \
.deps/obs-studio-*/libobs; do
if [ -n "$candidate" ] && [ -f "$candidate/obs.h" ]; then
obs_include="$candidate"
break
fi
done
if [ -z "$obs_include" ] && command -v pkg-config >/dev/null && pkg-config --exists libobs; then
obs_include=$(pkg-config --variable=includedir libobs)/obs
fi
if [ -z "$obs_include" ]; then
echo "libobs headers not found; run inside 'nix develop' (or set OBS_INCLUDE_DIR)" >&2
exit 1
fi
(cd ../.. && {{ cargo_compile }} check --locked --quiet -p libmoq)
# Ask cargo where its build script ran rather than reconstructing the path.
# CARGO_TARGET_DIR, a `--target` triple and a custom profile each move the
# generated header, and guessing wrong doesn't fail: it type-checks against
# whatever stale copy the default directory still holds. Replays the check
# above from cache, so it costs a process rather than a compile.
out_dir=$(cd ../.. && {{ cargo_compile }} check --locked -p libmoq --message-format=json |
jq -r 'select(.reason == "build-script-executed") | select(.package_id | tostring | test("libmoq")) | .out_dir' |
tail -1)
if [ -z "$out_dir" ]; then
echo "cargo reported no build script output for libmoq" >&2
exit 1
fi
# <target>/<profile>/build/libmoq-<hash>/out, and the header sits beside the
# profile directory. rs/libmoq/build.rs derives it from OUT_DIR the same way.
moq_include="$(dirname "$(dirname "$(dirname "$(dirname "$out_dir")")")")/include"
if [ ! -f "$moq_include/moq.h" ]; then
echo "$moq_include/moq.h missing after 'cargo check --locked -p libmoq'" >&2
exit 1
fi
# One flag per line, path glued to the -I, so a caller can rebuild the
# arguments without splitting on the spaces in a path.
printf -- '-I%s\n' src "$obs_include" "$moq_include"
# Unit-test the plugin sources against stubbed libobs/libmoq, under ThreadSanitizer.
#
# Manual, like `just rs macos` and `just rs windows`: PR CI links the plugin but
# doesn't run this, since ThreadSanitizer needs its own build. Run it whenever
# you touch src/, especially the session callback plumbing, whose orderings the
# build can't check.
test:
#!/usr/bin/env bash
set -euo pipefail
# Unlike the lint recipes, this one never skips: a test gate that reports
# success without running anything is worse than no gate, and nothing else
# covers this code.
cxx="${CXX:-c++}"
unsupported="'$cxx' cannot build and run -fsanitize=thread binaries. These tests need a
Clang or GCC whose ThreadSanitizer runtime works on this host: set CXX, or on Windows run
them from WSL, since neither MSVC nor Clang on Windows implements ThreadSanitizer."
if ! command -v "$cxx" >/dev/null; then
echo "no C++ compiler at '$cxx'." >&2
echo "$unsupported" >&2
exit 1
fi
out=$(mktemp -d)
trap 'rm -rf "$out"' EXIT
# Run the probe, don't just link it: a mismatch between the compiler's TSan
# runtime and the host kernel (macOS is where this shows up) links fine and
# then segfaults before main, which is an unreadable way for the real test
# to fail.
# The subshell keeps bash's own "Segmentation fault" job message out of the
# output, so the explanation below is all the reader gets.
if ! printf 'int main(){}\n' | "$cxx" -x c++ -fsanitize=thread -o "$out/probe" - >/dev/null 2>&1 \
|| ! ("$out/probe" >/dev/null 2>&1) 2>/dev/null; then
echo "$unsupported" >&2
exit 1
fi
includes_raw=$(just _includes)
includes=()
while IFS= read -r flag; do includes+=("$flag"); done <<< "$includes_raw"
"$cxx" -std=c++17 -g -O0 -fsanitize=thread "${includes[@]}" \
-o "$out/moq-output-test" test/moq-output-test.cpp src/moq-output.cpp
TSAN_OPTIONS="halt_on_error=1" "$out/moq-output-test"
# Lint formatting. Skips a tool silently if it isn't on $PATH (matches repo convention).
check:
#!/usr/bin/env bash
set -euo pipefail
if grep -Fq '"MOQ_VERSION"' CMakePresets.json; then
echo "CMakePresets.json must not pin MOQ_VERSION" >&2
exit 1
fi
# The headers `just obs compile` type-checks against are a second copy of
# the OBS version this spec downloads, so a bump here has to reach flake.nix
# or the two gates check different libobs.
spec_obs=$(jq -r '.dependencies["obs-studio"].version // ""' buildspec.json)
flake_obs=$(sed -n '/pname = "libobs-headers"/,/}/p' ../../flake.nix | sed -n 's/.*version = "\([^"]*\)".*/\1/p')
# Both are pattern matches against files this recipe doesn't own the shape
# of. An empty parse compares equal to an empty parse, so the guard would
# pass by reporting nothing rather than by finding the versions in sync.
if [ -z "$spec_obs" ] || [ -z "$flake_obs" ]; then
echo "couldn't read the obs-studio version: '$spec_obs' from buildspec.json, '$flake_obs' from flake.nix" >&2
exit 1
fi
if [ "$spec_obs" != "$flake_obs" ]; then
echo "obs-studio is $spec_obs in buildspec.json but $flake_obs in flake.nix (libobs-headers)" >&2
exit 1
fi
if command -v cmake >/dev/null; then
build_dir=$(mktemp -d)
trap 'rm -rf "$build_dir"' EXIT
if cmake -S . -B "$build_dir" -DMOQ_LOCAL= >"$build_dir/configure.log" 2>&1; then
echo "CMake accepted a release build without MOQ_VERSION" >&2
exit 1
fi
if ! grep -Fq "MOQ_VERSION is required when MOQ_LOCAL does not contain rs/libmoq" "$build_dir/configure.log"; then
cat "$build_dir/configure.log" >&2
exit 1
fi
fi
if command -v clang-format >/dev/null; then
git ls-files 'src/*.cpp' 'src/*.h' 'test/*.cpp' | xargs clang-format --dry-run --Werror
fi
if command -v gersemi >/dev/null; then
gersemi --check CMakeLists.txt cmake
fi
# Auto-fix formatting.
fix:
#!/usr/bin/env bash
set -euo pipefail
if command -v clang-format >/dev/null; then
git ls-files 'src/*.cpp' 'src/*.h' 'test/*.cpp' | xargs clang-format -i
fi
if command -v gersemi >/dev/null; then
gersemi --in-place CMakeLists.txt cmake
fi
# Detect the CMake preset for the current platform (or use the override).
# Pass `ci` as the second argument for the warnings-as-errors variant, whose
# name upstream spells differently on each platform.
preset override="" variant="":
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "{{ override }}" ]]; then
echo "{{ override }}"
elif [[ "$OSTYPE" == "darwin"* ]]; then
[[ "{{ variant }}" == "ci" ]] && echo "macos-ci" || echo "macos"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
[[ "{{ variant }}" == "ci" ]] && echo "ubuntu-ci-x86_64" || echo "ubuntu-x86_64"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
[[ "{{ variant }}" == "ci" ]] && echo "windows-ci-x64" || echo "windows-x64"
else
echo "Unknown platform: $OSTYPE" >&2
exit 1
fi