import sys;
import os;
import inspect;
import shutil;
from contextlib import contextmanager;
from invoke import task, call;
MEDIASOUP_BUILDTYPE = os.getenv('MEDIASOUP_BUILDTYPE') or 'Release';
WORKER_DIR = os.path.dirname(os.path.abspath(
inspect.getframeinfo(inspect.currentframe()).filename
));
MEDIASOUP_OUT_DIR = os.getenv('MEDIASOUP_OUT_DIR') or f'{WORKER_DIR}/out';
MEDIASOUP_INSTALL_DIR = os.getenv('MEDIASOUP_INSTALL_DIR') or f'{MEDIASOUP_OUT_DIR}/{MEDIASOUP_BUILDTYPE}';
BUILD_DIR = os.getenv('BUILD_DIR') or f'{MEDIASOUP_INSTALL_DIR}/build';
PIP_INVOKE_DIR = f'{MEDIASOUP_OUT_DIR}/pip_invoke';
PIP_MESON_NINJA_DIR = f'{MEDIASOUP_OUT_DIR}/pip_meson_ninja';
PIP_PYLINT_DIR = f'{MEDIASOUP_OUT_DIR}/pip_pylint';
NUM_CORES = len(os.sched_getaffinity(0)) if hasattr(os, 'sched_getaffinity') else os.cpu_count();
PYTHON = os.getenv('PYTHON') or sys.executable;
MESON = os.getenv('MESON') or f'{PIP_MESON_NINJA_DIR}/bin/meson';
MESON_VERSION = os.getenv('MESON_VERSION') or '1.9.1';
MESON_ARGS = os.getenv('MESON_ARGS') if os.getenv('MESON_ARGS') else '--vsenv' if os.name == 'nt' else '';
NINJA_VERSION = os.getenv('NINJA_VERSION') or '1.10.2.4';
PYLINT_VERSION = os.getenv('PYLINT_VERSION') or '3.0.2';
NPM = os.getenv('NPM') or 'npm';
DOCKER = os.getenv('DOCKER') or 'docker';
PTY_SUPPORTED = os.name != 'nt' and sys.stdout.isatty();
SHELL = '/bin/sh' if not os.name == 'nt' else None;
os.environ['PYTHONDONTWRITEBYTECODE'] = 'true';
if os.name == 'nt':
os.environ['NINJA'] = f'{PIP_MESON_NINJA_DIR}/bin/ninja.exe';
else:
os.environ['NINJA'] = f'{PIP_MESON_NINJA_DIR}/bin/ninja';
PYTHONPATH = os.getenv('PYTHONPATH') or '';
if os.name == 'nt':
os.environ['PYTHONPATH'] = f'{PIP_INVOKE_DIR};{PIP_MESON_NINJA_DIR};{PIP_PYLINT_DIR};{PYTHONPATH}';
else:
os.environ['PYTHONPATH'] = f'{PIP_INVOKE_DIR}:{PIP_MESON_NINJA_DIR}:{PIP_PYLINT_DIR}:{PYTHONPATH}';
@contextmanager
def cd_worker():
original_dir = os.getcwd()
os.chdir(WORKER_DIR)
try:
yield
finally:
os.chdir(original_dir)
@task
def meson_ninja(ctx):
if os.path.isfile(MESON):
return;
try:
ctx.run(
f'"{PYTHON}" -m pip install --system --upgrade --no-user --target "{PIP_MESON_NINJA_DIR}" pip setuptools',
echo=True,
hide=True,
shell=SHELL
);
except:
ctx.run(
f'"{PYTHON}" -m pip install --upgrade --no-user --target "{PIP_MESON_NINJA_DIR}" pip setuptools',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
pip_build_binaries = '--no-binary :all:' if os.path.isfile('/etc/NIXOS') or os.path.isdir('/etc/guix') else '';
ctx.run(
f'"{PYTHON}" -m pip install --upgrade --no-user --target "{PIP_MESON_NINJA_DIR}" {pip_build_binaries} meson=={MESON_VERSION} ninja=={NINJA_VERSION}',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[meson_ninja])
def setup(ctx, meson_args=MESON_ARGS):
if MEDIASOUP_BUILDTYPE == 'Release':
with cd_worker():
ctx.run(
f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype release -Db_ndebug=true {meson_args} "{BUILD_DIR}"',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
elif MEDIASOUP_BUILDTYPE == 'Debug':
with cd_worker():
ctx.run(
f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype debug {meson_args} "{BUILD_DIR}"',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
else:
with cd_worker():
ctx.run(
f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype {MEDIASOUP_BUILDTYPE} -Db_ndebug=if-release {meson_args} "{BUILD_DIR}"',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def clean(ctx):
shutil.rmtree(MEDIASOUP_INSTALL_DIR, ignore_errors=True);
@task
def clean_build(ctx):
shutil.rmtree(BUILD_DIR, ignore_errors=True);
@task
def clean_pip(ctx):
shutil.rmtree(PIP_MESON_NINJA_DIR, ignore_errors=True);
shutil.rmtree(PIP_PYLINT_DIR, ignore_errors=True);
@task(pre=[meson_ninja])
def clean_subprojects(ctx):
with cd_worker():
ctx.run(
f'"{MESON}" subprojects purge --include-cache --confirm',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def clean_all(ctx):
with cd_worker():
try:
ctx.run(
f'"{MESON}" subprojects purge --include-cache --confirm',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
except:
pass;
shutil.rmtree(MEDIASOUP_OUT_DIR, ignore_errors=True);
shutil.rmtree('include/FBS', ignore_errors=True);
@task(pre=[meson_ninja])
def update_wrap_file(ctx, subproject):
with cd_worker():
ctx.run(
f'"{MESON}" subprojects update --reset {subproject}',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[setup])
def flatc(ctx):
with cd_worker():
ctx.run(
f'"{MESON}" compile -C "{BUILD_DIR}" flatbuffers-generator',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[setup, flatc], default=True)
def mediasoup_worker(ctx):
if os.getenv('MEDIASOUP_WORKER_BIN'):
print('skipping mediasoup-worker compilation due to the existence of the MEDIASOUP_WORKER_BIN environment variable');
return;
with cd_worker():
ctx.run(
f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
with cd_worker():
ctx.run(
f'"{MESON}" install -C "{BUILD_DIR}" --no-rebuild --tags mediasoup-worker',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[setup, flatc])
def libmediasoup_worker(ctx):
with cd_worker():
ctx.run(
f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} libmediasoup-worker',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
with cd_worker():
ctx.run(
f'"{MESON}" install -C "{BUILD_DIR}" --no-rebuild --tags libmediasoup-worker',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[setup, flatc])
def xcode(ctx):
with cd_worker():
ctx.run(
f'"{MESON}" setup --buildtype {MEDIASOUP_BUILDTYPE.lower()} --backend xcode "{MEDIASOUP_OUT_DIR}/xcode"',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def lint(ctx):
with cd_worker():
ctx.run(
f'"{NPM}" run lint --prefix scripts/',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
if not os.path.isdir(PIP_PYLINT_DIR):
ctx.run(
f'"{PYTHON}" -m pip install --upgrade --no-user --target="{PIP_PYLINT_DIR}" pylint=={PYLINT_VERSION}',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
with cd_worker():
ctx.run(
f'"{PYTHON}" -m pylint tasks.py',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def format(ctx):
with cd_worker():
ctx.run(
f'"{NPM}" run format --prefix scripts/',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[call(setup, meson_args=MESON_ARGS + ' -Dms_build_tests=true')])
def tidy(ctx):
with cd_worker():
ctx.run(
f'"{NPM}" run tidy --prefix scripts/',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[call(setup, meson_args=MESON_ARGS + ' -Dms_build_tests=true')])
def tidy_fix(ctx):
with cd_worker():
ctx.run(
f'"{NPM}" run tidy:fix --prefix scripts/',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[call(setup, meson_args=MESON_ARGS + ' -Dms_build_tests=true'), flatc])
def test(ctx):
with cd_worker():
ctx.run(
f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker-test',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
with cd_worker():
ctx.run(
f'"{MESON}" install -C "{BUILD_DIR}" --no-rebuild --tags mediasoup-worker-test',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
mediasoup_worker_test = 'mediasoup-worker-test.exe' if os.name == 'nt' else 'mediasoup-worker-test';
mediasoup_test_tags = os.getenv('MEDIASOUP_TEST_TAGS') or '';
with cd_worker():
ctx.run(
f'"{BUILD_DIR}/{mediasoup_worker_test}" --invisibles --colour-mode=ansi {mediasoup_test_tags}',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task(pre=[call(setup, meson_args=MESON_ARGS + ' -Dms_build_tests=true -Db_sanitize=address -Db_lundef=false'), flatc])
def test_asan_address(ctx):
with cd_worker():
ctx.run(
f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker-test-asan-address',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
with cd_worker():
ctx.run(
f'"{MESON}" install -C "{BUILD_DIR}" --no-rebuild --tags mediasoup-worker-test-asan-address',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
mediasoup_test_tags = os.getenv('MEDIASOUP_TEST_TAGS') or '';
with cd_worker():
ctx.run(
f'"{BUILD_DIR}/mediasoup-worker-test-asan-address" --invisibles {mediasoup_test_tags}',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL,
env={**os.environ, 'ASAN_OPTIONS': 'halt_on_error=1:print_stacktrace=1:detect_leaks=1:symbolize=1:detect_stack_use_after_return=1:strict_init_order=1:check_initialization_order=1:detect_container_overflow=1'}
);
@task(pre=[call(setup, meson_args=MESON_ARGS + ' -Dms_build_tests=true -Db_sanitize=undefined -Db_lundef=false'), flatc])
def test_asan_undefined(ctx):
with cd_worker():
ctx.run(
f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker-test-asan-undefined',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
with cd_worker():
ctx.run(
f'"{MESON}" install -C "{BUILD_DIR}" --no-rebuild --tags mediasoup-worker-test-asan-undefined',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
mediasoup_test_tags = os.getenv('MEDIASOUP_TEST_TAGS') or '';
with cd_worker():
ctx.run(
f'"{BUILD_DIR}/mediasoup-worker-test-asan-undefined" --invisibles {mediasoup_test_tags}',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL,
env={**os.environ, 'UBSAN_OPTIONS': 'halt_on_error=1:print_stacktrace=1:suppressions=ubsan_suppressions.txt'}
);
@task(pre=[call(setup, meson_args=MESON_ARGS + ' -Db_sanitize=address -Db_lundef=false'), flatc])
def fuzzer(ctx):
with cd_worker():
ctx.run(
f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker-fuzzer',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
with cd_worker():
ctx.run(
f'"{MESON}" install -C "{BUILD_DIR}" --no-rebuild --tags mediasoup-worker-fuzzer',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def fuzzer_run_all(ctx):
with cd_worker():
ctx.run(
f'LSAN_OPTIONS=verbosity=1:log_threads=1 "{BUILD_DIR}/mediasoup-worker-fuzzer" -artifact_prefix=fuzzer/reports/ -max_len=1400 fuzzer/new-corpus deps/webrtc-fuzzer-corpora/corpora/stun-corpus deps/webrtc-fuzzer-corpora/corpora/rtp-corpus deps/webrtc-fuzzer-corpora/corpora/rtcp-corpus',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def docker(ctx):
if os.getenv('DOCKER_NO_CACHE') == 'true':
with cd_worker():
ctx.run(
f'"{DOCKER}" build -f Dockerfile --no-cache --tag mediasoup/docker:latest .',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
else:
with cd_worker():
ctx.run(
f'"{DOCKER}" build -f Dockerfile --tag mediasoup/docker:latest .',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def docker_run(ctx):
with cd_worker():
ctx.run(
f'"{DOCKER}" run --name=mediasoupDocker -it --rm --privileged --cap-add SYS_PTRACE -v "{WORKER_DIR}/../:/foo bar/mediasoup" mediasoup/docker:latest',
echo=True,
pty=True, shell=SHELL
);
@task
def docker_alpine(ctx):
if os.getenv('DOCKER_NO_CACHE') == 'true':
with cd_worker():
ctx.run(
f'"{DOCKER}" build -f Dockerfile.alpine --no-cache --tag mediasoup/docker-alpine:latest .',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
else:
with cd_worker():
ctx.run(
f'"{DOCKER}" build -f Dockerfile.alpine --tag mediasoup/docker-alpine:latest .',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def docker_alpine_run(ctx):
with cd_worker():
ctx.run(
f'"{DOCKER}" run --name=mediasoupDockerAlpine -it --rm --privileged --cap-add SYS_PTRACE -v "{WORKER_DIR}/../:/foo bar/mediasoup" mediasoup/docker-alpine:latest',
echo=True,
pty=True, shell=SHELL
);
@task
def docker_386(ctx):
if os.getenv('DOCKER_NO_CACHE') == 'true':
with cd_worker():
ctx.run(
f'"{DOCKER}" build --platform linux/386 -f Dockerfile.386 --no-cache --tag mediasoup/docker-386:latest .',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
else:
with cd_worker():
ctx.run(
f'"{DOCKER}" build --platform linux/386 -f Dockerfile.386 --tag mediasoup/docker-386:latest .',
echo=True,
pty=PTY_SUPPORTED,
shell=SHELL
);
@task
def docker_386_run(ctx):
with cd_worker():
ctx.run(
f'"{DOCKER}" run --name=mediasoupDocker386 -it --rm --privileged --cap-add SYS_PTRACE -v "{WORKER_DIR}/../:/foo bar/mediasoup" mediasoup/docker-386:latest',
echo=True,
pty=True, shell=SHELL
);