From 3f0594d33b1d0638a474327d9a1ce9e3d223c809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Baz=20Castillo?= Date: Mon, 27 Nov 2023 13:27:11 +0100 Subject: [PATCH] Replace make + Makefile with Python Invoke library + tasks.py ((also fix installation under path with whitespaces) (#1239) --- .github/workflows/{codeql.yml => codeql.yaml} | 3 + .../workflows/mediasoup-worker-prebuild.yaml | 5 + .github/workflows/mediasoup-worker.yaml | 24 +- .gitignore | 5 +- CHANGELOG.md | 5 + doc/Building.md | 137 ++-- npm-scripts.mjs | 179 +++--- package.json | 16 +- worker/Dockerfile | 11 +- worker/Dockerfile.alpine | 7 +- worker/Makefile | 288 ++------- worker/build.rs | 101 ++- worker/scripts/clang-format.mjs | 4 +- worker/scripts/cpu_cores.sh | 19 - worker/scripts/get-dep.sh | 2 +- worker/scripts/getmake.py | 28 - worker/tasks.py | 602 ++++++++++++++++++ 17 files changed, 918 insertions(+), 518 deletions(-) rename .github/workflows/{codeql.yml => codeql.yaml} (96%) delete mode 100755 worker/scripts/cpu_cores.sh delete mode 100644 worker/scripts/getmake.py create mode 100644 worker/tasks.py diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yaml similarity index 96% rename from .github/workflows/codeql.yml rename to .github/workflows/codeql.yaml index b26646fd42..15c104f781 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yaml @@ -55,6 +55,9 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality + # We need to install pip invoke library. + - name: pip3 install invoke + run: pip3 install invoke # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below) diff --git a/.github/workflows/mediasoup-worker-prebuild.yaml b/.github/workflows/mediasoup-worker-prebuild.yaml index 39392aae04..ea1491cf72 100644 --- a/.github/workflows/mediasoup-worker-prebuild.yaml +++ b/.github/workflows/mediasoup-worker-prebuild.yaml @@ -54,6 +54,11 @@ jobs: - name: npm ci run: npm ci --ignore-scripts --omit=dev + # However we also need to install pip invoke manually (since + # `--ignore-scripts` prevented invoke from being installed). + - name: pip3 install invoke + run: pip3 install invoke + - name: npm run worker:build run: npm run worker:build diff --git a/.github/workflows/mediasoup-worker.yaml b/.github/workflows/mediasoup-worker.yaml index f4e3bc16b6..ac0cb4d042 100644 --- a/.github/workflows/mediasoup-worker.yaml +++ b/.github/workflows/mediasoup-worker.yaml @@ -64,25 +64,25 @@ jobs: restore-keys: | ${{ matrix.build.os }}-node-${{matrix.build.cc}}- - # We need to install some NPM production deps for npm-scripts.mjs to work. - - name: npm ci - run: npm ci --ignore-scripts --omit=dev + # We need to install pip invoke manually. + - name: pip3 install invoke + run: pip3 install invoke - # We need to install deps on worker/scripts/package.json. - - name: npm run install-worker-dev-tools - run: npm run install-worker-dev-tools + # We need to install npm deps of worker/scripts/package.json. + - name: npm ci --prefix worker/scripts + run: npm ci --prefix worker/scripts # TODO: Maybe fix this one day. if: runner.os != 'Windows' - - name: npm run lint:worker - run: npm run lint:worker + - name: invoke -r worker lint + run: invoke -r worker lint # TODO: Maybe fix this one day. if: runner.os != 'Windows' - - name: npm run worker:build - run: npm run worker:build + - name: invoke -r worker mediasoup-worker + run: invoke -r worker mediasoup-worker - - name: npm run test:worker - run: npm run test:worker + - name: invoke -r worker test + run: invoke -r worker test # TODO: Maybe fix this one day. if: runner.os != 'Windows' diff --git a/.gitignore b/.gitignore index abbaa0f7f2..e2f65fca29 100644 --- a/.gitignore +++ b/.gitignore @@ -20,10 +20,11 @@ # Flatc generated files. /worker/include/FBS /worker/prebuild -# Vistual Studio generated Stuff. +# Python invoke. +/worker/pip_invoke +# Build artifacts. /worker/**/Debug /worker/**/Release -/worker/.vs # clang-fuzzer stuff is too big. /worker/deps/clang-fuzzer # Ignore all fuzzer generated test inputs. diff --git a/CHANGELOG.md b/CHANGELOG.md index 115b1b6900..59b0bfeb9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog +### NEXT + +* Replace make + Makefile with Python Invoke library + tasks.py (also fix installation under path with whitespaces) ([PR #1239](https://github.com/versatica/mediasoup/pull/1239)). + + ### 3.13.5 * Fix RTCP SDES packet size calculation ([PR #1236](https://github.com/versatica/mediasoup/pull/1236) based on PR [PR #1234](https://github.com/versatica/mediasoup/pull/1234) by @ybybwdwd). diff --git a/doc/Building.md b/doc/Building.md index 35b362765a..7408111f9e 100644 --- a/doc/Building.md +++ b/doc/Building.md @@ -16,11 +16,11 @@ Compiles mediasoup TypeScript code (`lib` folder) JavaScript, places it into the ### `npm run worker:build` -Builds the `mediasoup-worker` binary. It invokes `make`below. +Builds the `mediasoup-worker` binary. It invokes `invoke`below. ### `npm run worker:prebuild` -Creates a prebuilt of `mediasoup-worker` in the `worker/prebuild` folder. +Creates a prebuilt of `mediasoup-worker` binary in the `worker/prebuild` folder. ### `npm run lint` @@ -32,11 +32,23 @@ Validates mediasoup JavaScript files using [ESLint](https://eslint.org). ### `npm run lint:worker` -Validates mediasoup-worker C++ files using [clang-format](https://clang.llvm.org/docs/ClangFormat.html). It invokes `make lint` below. +Validates mediasoup worker C++ files using [clang-format](https://clang.llvm.org/docs/ClangFormat.html). It invokes `invoke lint` below. ### `npm run format:worker` -Rewrites mediasoup-worker C++ files using [clang-format](https://clang.llvm.org/docs/ClangFormat.html). It invokes `make format` below. +Rewrites mediasoup worker C++ files using [clang-format](https://clang.llvm.org/docs/ClangFormat.html). It invokes `invoke format` below. + +### `npm run flatc` + +Runs both `npm run flatc:node` and `npm run flatc:worker`. + +### `npm run flatc:node` + +Compiles [FlatBuffers](https://github.com/google/flatbuffers) `.fbs` files in `worker/fbs` to TypeScript code. + +### `npm run flatc:worker` + +Compiles [FlatBuffers](https://github.com/google/flatbuffers) `.fbs` files in `worker/fbs` to C++ code. ### `npm run test` @@ -48,129 +60,136 @@ Runs [Jest](https://jestjs.io) test units located at `test/` folder. ### `npm run test:worker` -Runs [Catch2](https://github.com/catchorg/Catch2) test units located at `worker/test/` folder. It invokes `make test` below. +Runs [Catch2](https://github.com/catchorg/Catch2) test units located at `worker/test/` folder. It invokes `invoke test` below. ### `npm run coverage:node` Same as `npm run test:node` but it also opens a browser window with JavaScript coverage results. -### `npm run install-deps:node` +### `npm run release:check` -Installs NPM dependencies and updates `package-lock.json`. - -### `npm run install-worker-dev-tools` - -Installs worker NPM packages needed for local development. +Runs linters and tests in Node and C++ code. ## Rust -The only special feature in Rust case is special environment variable `"KEEP_BUILD_ARTIFACTS", that when set to `1` will allow incremental recompilation of changed C++ sources during hacking on mediasoup. +The only special feature in Rust case is special environment variable "KEEP_BUILD_ARTIFACTS", that when set to "1" will allow incremental recompilation of changed C++ sources during hacking on mediasoup. + It is not necessary for normal usage of mediasoup as a dependency. -## Makefile +## Python Invoke and `tasks.py` file + +mediasoup uses Python [Invoke](https://www.pyinvoke.org/) library for managing and organizing tasks in the `worker` folder (mediasoup worker C++ subproject). `Invoke` is basically a replacemente of `make` + `Makefile` written in Python. mediasoup automatically installs `Invoke` in a local custom path during the installation process (in both Node and Rust) so the user doesn't need to worry about it. -The `worker` folder contains a `Makefile` for the mediasoup-worker C++ subproject. It includes the following tasks: +Tasks are defined in `worker/tasks.py`. For development purposes, developers or contributors can install `Invoke` using `pip3 install invoke` and run tasks below within the `worker` folder. -### `make` or `make mediasoup-worker` +See all the tasks by running `invoke --list` within the `worker` folder. -Alias of ``make mediasoup-worker` below. +*NOTE:* For some of these tasks to work, npm dependencies of `worker/scripts/package.json` must be installed: -### `make meson-ninja` +```bash +npm ci --prefix worker/scripts +``` + +### `invoke` (default task) -Installs `meson` and `ninja`. +Alias of `invoke mediasoup-worker` task below. -### `make clean` +### `invoke meson-ninja` + +Installs `meson` and `ninja` into a local custom path. + +### `invoke clean` Cleans built objects and binaries. -### `make clean-build` +### `invoke clean-build` Cleans built objects and other artifacts, but keeps `mediasoup-worker` binary in place. -### `make clean-pip` +### `invoke clean-pip` Cleans `meson` and `ninja` installed in local prefix with pip. -### `make clean-subprojects` +### `invoke clean-subprojects` Cleans subprojects downloaded with Meson. -### `make clean-all` +### `invoke clean-all` Cleans built objects and binaries, `meson` and `ninja` installed in local prefix with pip and all subprojects downloaded with Meson. -### `make update-wrap-file` +### `invoke update-wrap-file [subproject]` -Update the wrap file of a subproject with Meson. Usage example: +Updates the wrap file of a subproject (those in `worker/subprojects` folder) with Meson. Usage example: ```bash cd worker -make update-wrap-file SUBPROJECT=openssl +invoke update-wrap-file openssl ``` -### `make mediasoup-worker` +### `invoke mediasoup-worker` Builds the `mediasoup-worker` binary at `worker/out/Release/`. If the "MEDIASOUP_MAX_CORES" environment variable is set, the build process will use that number of CPU cores. Otherwise it will auto-detect the number of cores in the machine. -"MEDIASOUP_BUILDTYPE" environment variable controls build types, `Release` and `Debug` are presets optimized for those use cases. -Other build types are possible too, but they are not presets and will require "MESON_ARGS" use to customize build configuration. +"MEDIASOUP_BUILDTYPE" environment variable controls build types, "Release" and "Debug" are presets optimized for those use cases. Other build types are possible too, but they are not presets and will require "MESON_ARGS" use to customize build configuration. + Check the meaning of useful macros in the `worker/include/Logger.hpp` header file if you want to enable tracing or other debug information. Binary is built at `worker/out/MEDIASOUP_BUILDTYPE/build`. -In order to instruct the mediasoup Node.js module to use the `Debug` mediasoup-worker binary, an environment variable must be set before running the Node.js application: +In order to instruct the mediasoup Node.js module to use the "Debug"` `mediasoup-worker` binary, an environment variable must be set before running the Node.js application: ```bash MEDIASOUP_BUILDTYPE=Debug node myapp.js ``` -If the "MEDIASOUP_WORKER_BIN" environment variable is set (it must be an absolute file path), mediasoup will use the it as mediasoup-worker binary and **won't** compile the binary: +If the "MEDIASOUP_WORKER_BIN" environment variable is set (it must be an absolute file path), mediasoup will use the it as `mediasoup-worker` binary and **won't** compile the binary: ```bash MEDIASOUP_WORKER_BIN="/home/xxx/src/foo/mediasoup-worker" node myapp.js ``` -### `make libmediasoup-worker` +### `invoke libmediasoup-worker` Builds the `libmediasoup-worker` static library at `worker/out/Release/`. "MEDIASOUP_MAX_CORES"` and "MEDIASOUP_BUILDTYPE" environment variables from above still apply for static library build. -### `make xcode` +### `invoke xcode` -Builds a Xcode project for the mediasoup-worker subproject. +Builds a Xcode project for the mediasoup worker subproject. -### `make lint` +### `invoke lint` -Validates mediasoup-worker C++ files using [clang-format](https://clang.llvm.org/docs/ClangFormat.html) and rules in `worker/.clang-format`. +Validates mediasoup worker C++ files using [clang-format](https://clang.llvm.org/docs/ClangFormat.html) and rules in `worker/.clang-format`. -### `make format` +### `invoke format` -Rewrites mediasoup-worker C++ files using [clang-format](https://clang.llvm.org/docs/ClangFormat.html). +Rewrites mediasoup worker C++ files using [clang-format](https://clang.llvm.org/docs/ClangFormat.html). -### `make test` +### `invoke test` Builds and runs the `mediasoup-worker-test` binary at `worker/out/Release/` (or at `worker/out/Debug/` if the "MEDIASOUP_BUILDTYPE" environment variable is set to "Debug"), which uses [Catch2](https://github.com/catchorg/Catch2) to run test units located at `worker/test/` folder. -### `make test-asan` +### `invoke test-asan` Run test with Address Sanitizer. -### `make tidy` +### `invoke tidy` Runs [clang-tidy](http://clang.llvm.org/extra/clang-tidy/) and performs C++ code checks following `worker/.clang-tidy` rules. **Requirements:** -* `make clean` and `make` must have been called first. +* `invoke clean` and `invoke mediasoup-worker` must have been called first. * [PyYAML](https://pyyaml.org/) is required. - In OSX install it with `brew install libyaml` and `sudo easy_install-X.Y pyyaml`. "MEDIASOUP_TIDY_CHECKS" environment variable with a comma separated list of checks overrides the checks defined in `.clang-tidy` file. -### `make fuzzer` +### `invoke fuzzer` Builds the `mediasoup-worker-fuzzer` binary (which uses [libFuzzer](http://llvm.org/docs/LibFuzzer.html)) at `worker/out/Release/` (or at `worker/out/Debug/` if the "MEDIASOUP_BUILDTYPE" environment variable is set to "Debug"). @@ -182,11 +201,11 @@ Builds the `mediasoup-worker-fuzzer` binary (which uses [libFuzzer](http://llvm. Read the [Fuzzer](Fuzzer.md) documentation for detailed information. -### `make fuzzer-run-all` +### `invoke fuzzer-run-all` Runs all fuzzer cases. -### `make docker` +### `invoke docker` Builds a Linux Ubuntu Docker image with fuzzer capable clang++ and all dependencies to run mediasoup. @@ -194,21 +213,33 @@ Builds a Linux Ubuntu Docker image with fuzzer capable clang++ and all dependenc ```bash cd worker -./scripts/get-dep.sh clang-fuzzer +scripts/get-dep.sh clang-fuzzer ``` -### `make docker-run` +### `invoke docker-run` -Runs a container of the Ubuntu Docker image created with `make docker`. It automatically executes a `bash` session in the `/mediasoup` directory, which is a Docker volume that points to the real `mediasoup` directory. +Runs a container of the Ubuntu Docker image created with `invoke docker`. It automatically executes a `bash` session in the `/mediasoup` directory, which is a Docker volume that points to the real `mediasoup` directory. -**NOTE:** To install and run mediasoup in the container, previous installation (if any) must be properly cleaned by entering the `worker` directory and running `make clean-all`. +**NOTE:** To install and run mediasoup in the container, previous installation (if any) must be properly cleaned by entering the `worker` directory and running `invoke clean-all`. -### `make docker-alpine` +### `invoke docker-alpine` Builds a Linux Alpine Docker image with all dependencies to run mediasoup. -### `make docker-alpine-run` +### `invoke docker-alpine-run` + +Runs a container of the Alpine Docker image created with `invoke docker-alpine`. It automatically executes an `ash` session in the `/mediasoup` directory, which is a Docker volume that points to the real `mediasoup` directory. + +**NOTE:** To install and run mediasoup in the container, previous installation (if any) must be properly cleaned by entering the `worker` directory and running `invoke clean-all`. + +## Makefile + +The `worker` folder contains a `Makefile` file for the mediasoup worker C++ subproject. It acts as a proxy to the `Invoke` tasks defined in `tasks.py`. The `Makefile` file exists to help developers or contributors that prefer keep using `make` commands. -Runs a container of the Alpine Docker image created with `make docker-alpine`. It automatically executes an `ash` session in the `/mediasoup` directory, which is a Docker volume that points to the real `mediasoup` directory. +All tasks defined in `tasks.py` (see above) are available in `Makefile`. There is only one exception: -**NOTE:** To install and run mediasoup in the container, previous installation (if any) must be properly cleaned by entering the `worker` directory and running `make clean-all`. +- The `update-wrap-file` needs a "SUBPROJECT" environment variable indicating the subproject to update. Usage example: + ```bash + cd worker + make update-wrap-file SUBPROJECT=openssl + ``` diff --git a/npm-scripts.mjs b/npm-scripts.mjs index 2de2298c8a..80ebc35c3e 100644 --- a/npm-scripts.mjs +++ b/npm-scripts.mjs @@ -2,15 +2,16 @@ import process from 'node:process'; import os from 'node:os'; import fs from 'node:fs'; import path from 'node:path'; -import { execSync, spawnSync } from 'node:child_process'; +import { execSync } from 'node:child_process'; import fetch from 'node-fetch'; import tar from 'tar'; const PKG = JSON.parse(fs.readFileSync('./package.json').toString()); -const IS_FREEBSD = os.platform() === 'freebsd'; const IS_WINDOWS = os.platform() === 'win32'; const MAYOR_VERSION = PKG.version.split('.')[0]; -const MAKE = process.env.MAKE || (IS_FREEBSD ? 'gmake' : 'make'); +const PYTHON = getPython(); +const PIP_INVOKE_DIR = path.resolve('worker/pip_invoke'); +const INVOKE_VERSION = process.env.INVOKE_VERSION ?? '2.2.0'; const FLATBUFFERS_VERSION = '23.3.3'; const WORKER_RELEASE_DIR = 'worker/out/Release'; const WORKER_RELEASE_BIN = IS_WINDOWS ? 'mediasoup-worker.exe' : 'mediasoup-worker'; @@ -23,12 +24,37 @@ const GH_REPO = 'mediasoup'; const task = process.argv.slice(2).join(' '); +// PYTHONPATH env must be updated now so all invoke calls below will find the +// pip invoke module. +if (process.env.PYTHONPATH) +{ + if (IS_WINDOWS) + { + process.env.PYTHONPATH = `${PIP_INVOKE_DIR};${process.env.PYTHONPATH}`; + } + else + { + process.env.PYTHONPATH = `${PIP_INVOKE_DIR}:${process.env.PYTHONPATH}`; + } +} +else +{ + process.env.PYTHONPATH = PIP_INVOKE_DIR; +} + run(); async function run() { switch (task) { + case 'preinstall': + { + installInvoke(); + + break; + } + // As per NPM documentation (https://docs.npmjs.com/cli/v9/using-npm/scripts) // `prepare` script: // @@ -142,7 +168,7 @@ async function run() case 'format:worker': { - executeCmd(`${MAKE} format -C worker`); + executeCmd(`"${PYTHON}" -m invoke -r worker format`); break; } @@ -185,20 +211,6 @@ async function run() break; } - case 'install-deps:node': - { - installNodeDeps(); - - break; - } - - case 'install-worker-dev-tools': - { - executeCmd('npm ci --prefix worker/scripts'); - - break; - } - case 'release:check': { checkRelease(); @@ -274,6 +286,42 @@ async function run() } } +function getPython() +{ + let python = process.env.PYTHON; + + if (!python) + { + try + { + execSync('python3 --version', { stdio: [ 'ignore', 'ignore', 'ignore' ] }); + python = 'python3'; + } + catch (error) + { + python = 'python'; + } + } + + return python; +} + +function installInvoke() +{ + if (fs.existsSync(PIP_INVOKE_DIR)) + { + return; + } + + logInfo('installInvoke()'); + + // Install pip invoke into custom location, so we don't depend on system-wide + // installation. + executeCmd( + `"${PYTHON}" -m pip install --upgrade --target="${PIP_INVOKE_DIR}" invoke==${INVOKE_VERSION}`, /* exitOnError */ true + ); +} + function deleteNodeLib() { if (!fs.existsSync('node/lib')) @@ -311,22 +359,7 @@ function buildWorker() { logInfo('buildWorker()'); - if (IS_WINDOWS) - { - if (!fs.existsSync('worker/out/msys/bin/make.exe')) - { - installMsysMake(); - } - - const msysPath = `${process.cwd()}\\worker\\out\\msys\\bin`; - - if (!process.env.PATH.includes(msysPath)) - { - process.env.PATH = `${msysPath};${process.env.PATH}`; - } - } - - executeCmd(`${MAKE} -C worker`); + executeCmd(`"${PYTHON}" -m invoke -r worker mediasoup-worker`); } function cleanWorkerArtifacts() @@ -334,19 +367,11 @@ function cleanWorkerArtifacts() logInfo('cleanWorkerArtifacts()'); // Clean build artifacts except `mediasoup-worker`. - executeCmd(`${MAKE} clean-build -C worker`); + executeCmd(`"${PYTHON}" -m invoke -r worker clean-build`); // Clean downloaded dependencies. - executeCmd(`${MAKE} clean-subprojects -C worker`); + executeCmd(`"${PYTHON}" -m invoke -r worker clean-subprojects`); // Clean PIP/Meson/Ninja. - executeCmd(`${MAKE} clean-pip -C worker`); - - if (IS_WINDOWS) - { - if (fs.existsSync('worker/out/msys')) - { - executeCmd('rd /s /q worker\\out\\msys'); - } - } + executeCmd(`"${PYTHON}" -m invoke -r worker clean-pip`); } function lintNode() @@ -360,7 +385,7 @@ function lintWorker() { logInfo('lintWorker()'); - executeCmd(`${MAKE} lint -C worker`); + executeCmd(`"${PYTHON}" -m invoke -r worker lint`); } function flatcNode() @@ -368,24 +393,24 @@ function flatcNode() logInfo('flatcNode()'); // Build flatc if needed. - executeCmd(`${MAKE} -C worker flatc`); + executeCmd(`"${PYTHON}" -m invoke -r worker flatc`); const buildType = process.env.MEDIASOUP_BUILDTYPE || 'Release'; const extension = IS_WINDOWS ? '.exe' : ''; const flatc = path.resolve(path.join( 'worker', 'out', buildType, 'build', 'subprojects', `flatbuffers-${FLATBUFFERS_VERSION}`, `flatc${extension}`)); - const src = path.resolve(path.join('worker', 'fbs', '*.fbs')); const out = path.resolve(path.join('node', 'src')); - const options = '--ts-no-import-ext --gen-object-api'; - const command = `${flatc} --ts ${options} -o ${out} `; - if (IS_WINDOWS) + for (const dirent of fs.readdirSync(path.join('worker', 'fbs'), { withFileTypes: true })) { - executeCmd(`for %f in (${src}) do ${command} %f`); - } - else - { - executeCmd(`for file in ${src}; do ${command} \$\{file\}; done`); + if (!dirent.isFile() || path.parse(dirent.name).ext !== '.fbs') + { + continue; + } + + const filePath = path.resolve(path.join('worker', 'fbs', dirent.name)); + + executeCmd(`"${flatc}" --ts --ts-no-import-ext --gen-object-api -o "${out}" "${filePath}"`); } } @@ -393,7 +418,7 @@ function flatcWorker() { logInfo('flatcWorker()'); - executeCmd(`${MAKE} -C worker flatc`); + executeCmd(`"${PYTHON}" -m invoke -r worker flatc`); } function testNode() @@ -406,7 +431,7 @@ function testNode() } else { - executeCmd(`jest --testPathPattern ${process.env.TEST_FILE}`); + executeCmd(`jest --testPathPattern "${process.env.TEST_FILE}"`); } } @@ -414,7 +439,7 @@ function testWorker() { logInfo('testWorker()'); - executeCmd(`${MAKE} test -C worker`); + executeCmd(`"${PYTHON}" -m invoke -r worker test`); } function installNodeDeps() @@ -441,42 +466,6 @@ function checkRelease() testWorker(); } -function installMsysMake() -{ - logInfo('installMsysMake()'); - - let pythonPath; - - // If PYTHON environment variable is given, use it. - if (process.env.PYTHON) - { - pythonPath = process.env.PYTHON; - } - // Otherwise ensure python3.exe is available in the PATH. - else - { - let res = spawnSync('where', [ 'python3.exe' ]); - - if (res.status !== 0) - { - res = spawnSync('where', [ 'python.exe' ]); - - if (res.status !== 0) - { - logError('`installMsysMake() | cannot find Python executable'); - - exitWithError(); - } - } - - pythonPath = String(res.stdout).trim(); - } - - const dir = path.resolve('worker/out/msys'); - - executeCmd(`${pythonPath} worker\\scripts\\getmake.py --dir="${dir}"`); -} - function ensureDir(dir) { logInfo(`ensureDir() [dir:${dir}]`); diff --git a/package.json b/package.json index 625cda38ed..c21cb41714 100644 --- a/package.json +++ b/package.json @@ -21,19 +21,20 @@ "types": "node/lib/index.d.ts", "files": [ "node/lib", - "worker/deps/libwebrtc", + "worker/src", + "worker/include", "worker/fbs", - "worker/fuzzer/include", + "worker/test/src", + "worker/test/include", "worker/fuzzer/src", - "worker/include", + "worker/fuzzer/include", "worker/scripts/*.mjs", "worker/scripts/*.json", "worker/scripts/*.py", "worker/scripts/*.sh", - "worker/src", "worker/subprojects/*.wrap", - "worker/test/include", - "worker/test/src", + "worker/deps/libwebrtc", + "worker/tasks.py", "worker/Makefile", "worker/meson.build", "worker/meson_options.txt", @@ -49,6 +50,7 @@ "nodejs" ], "scripts": { + "preinstall": "node npm-scripts.mjs preinstall", "prepare": "node npm-scripts.mjs prepare", "postinstall": "node npm-scripts.mjs postinstall", "typescript:build": "node npm-scripts.mjs typescript:build", @@ -66,8 +68,6 @@ "test:node": "node npm-scripts.mjs test:node", "test:worker": "node npm-scripts.mjs test:worker", "coverage:node": "node npm-scripts.mjs coverage:node", - "install-deps:node": "node npm-scripts.mjs install-deps:node", - "install-worker-dev-tools": "node npm-scripts.mjs install-worker-dev-tools", "release:check": "node npm-scripts.mjs release:check", "release": "node npm-scripts.mjs release", "release:upload-mac-arm-prebuilt-worker": "node npm-scripts.mjs release:upload-mac-arm-prebuilt-worker" diff --git a/worker/Dockerfile b/worker/Dockerfile index df9193063d..81f4294115 100644 --- a/worker/Dockerfile +++ b/worker/Dockerfile @@ -1,15 +1,12 @@ FROM ubuntu:22.04 # Install dependencies. -RUN \ - set -x \ +RUN set -x \ && apt-get update \ && apt-get install --yes \ - bash-completion wget curl subversion screen gcc g++ cmake ninja-build golang \ - autoconf libtool apache2 python3-pip python3-dev pkg-config zlib1g-dev \ - libgss-dev libssl-dev libxml2-dev nasm libarchive-dev make automake \ - libdbus-1-dev libboost-dev autoconf-archive bash-completion python3-yaml \ - clang + gcc g++ clang pkg-config bash-completion wget curl \ + screen python3-pip python3-yaml pkg-config zlib1g-dev \ + libgss-dev libssl-dev libxml2-dev # Install node 20. RUN set -x \ diff --git a/worker/Dockerfile.alpine b/worker/Dockerfile.alpine index 5ae43d88c1..d1b31a6062 100644 --- a/worker/Dockerfile.alpine +++ b/worker/Dockerfile.alpine @@ -1,11 +1,8 @@ FROM alpine # Install dependencies. -RUN \ - set -x \ - && apk add \ - nodejs-current npm python3 py3-pip \ - make gcc g++ +RUN set -x \ + && apk add gcc g++ nodejs-current npm python3 py3-pip # Make CC and CXX point to gcc/g++. ENV LANG="C.UTF-8" diff --git a/worker/Makefile b/worker/Makefile index 820bac829c..7a0492128c 100644 --- a/worker/Makefile +++ b/worker/Makefile @@ -1,72 +1,23 @@ # # make tasks for mediasoup-worker. # - -# We need Python 3 here. -PYTHON ?= $(shell command -v python3 2> /dev/null || echo python) -ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) -CORES ?= $(shell ${ROOT_DIR}/scripts/cpu_cores.sh || echo 4) -MEDIASOUP_OUT_DIR ?= $(shell pwd)/out -# Controls build types, `Release` and `Debug` are presets optimized for those -# use cases. Other build types are possible too, but they are not presets and -# will require `MESON_ARGS` use to customize build configuration. -# Check the meaning of useful macros in the `worker/include/Logger.hpp` header -# file if you want to enable tracing or other debug information. -MEDIASOUP_BUILDTYPE ?= Release -LCOV = ./deps/lcov/bin/lcov -DOCKER ?= docker -PIP_DIR = $(MEDIASOUP_OUT_DIR)/pip -INSTALL_DIR ?= $(MEDIASOUP_OUT_DIR)/$(MEDIASOUP_BUILDTYPE) -BUILD_DIR ?= $(INSTALL_DIR)/build -MESON ?= $(PIP_DIR)/bin/meson -MESON_VERSION ?= 1.2.1 -# `MESON_ARGS` can be used to provide extra configuration parameters to Meson, -# such as adding defines or changing optimization options. For instance, use -# `MESON_ARGS="-Dms_log_trace=true -Dms_log_file_line=true" npm i` to compile -# worker with tracing and enabled. +# NOTE: This Makefile is a proxy to pip invoke commands (see tasks.py). # -# NOTE: On Windows make sure to add `--vsenv` or have MSVS environment already -# active if you override this parameter. -MESON_ARGS ?= "" -# Workaround for NixOS and Guix that don't work with pre-built binaries, see: -# https://github.com/NixOS/nixpkgs/issues/142383. -PIP_BUILD_BINARIES = $(shell [ -f /etc/NIXOS -o -d /etc/guix ] && echo "--no-binary :all:") -# Let's use a specific version of ninja to avoid buggy version 1.11.1: -# https://mediasoup.discourse.group/t/partly-solved-could-not-detect-ninja-v1-8-2-or-newer/ -# https://github.com/ninja-build/ninja/issues/2211 -# https://github.com/ninja-build/ninja/issues/2212 -NINJA_VERSION ?= 1.10.2.4 -NPM ?= npm -# Disable `*.pyc` files creation. -export PYTHONDONTWRITEBYTECODE = 1 -# Instruct `meson` where to look for ninja binary. -ifeq ($(OS),Windows_NT) - # Windows is, of course, special. - export NINJA = $(PIP_DIR)/bin/ninja.exe -else - export NINJA = $(PIP_DIR)/bin/ninja -endif +PYTHON ?= $(shell command -v python3 2> /dev/null || echo python) +PIP_INVOKE_DIR = $(shell pwd)/pip_invoke +INVOKE_VERSION ?= 2.2.0 -# Instruct Python where to look for modules it needs, such that `meson` actually -# runs from installed location. -# -# NOTE: For some reason on Windows adding `:${PYTHONPATH}` breaks things. +# Instruct Python where to look for invoke module. ifeq ($(OS),Windows_NT) - export PYTHONPATH := $(PIP_DIR) + export PYTHONPATH := $(PIP_INVOKE_DIR);${PYTHONPATH} else - export PYTHONPATH := $(PIP_DIR):${PYTHONPATH} -endif - -# Activate VS environment on Windows by default. -ifeq ($(OS),Windows_NT) -ifeq ($(MESON_ARGS),"") - MESON_ARGS = $(subst $\",,"--vsenv") -endif + export PYTHONPATH := $(PIP_INVOKE_DIR):${PYTHONPATH} endif .PHONY: \ default \ + invoke \ meson-ninja \ setup \ clean \ @@ -93,196 +44,79 @@ endif default: mediasoup-worker -meson-ninja: -ifeq ($(wildcard $(PIP_DIR)),) - # Updated pip and setuptools are needed for meson. - # `--system` is not present everywhere and is only needed as workaround for - # Debian-specific issue (copied from https://github.com/gluster/gstatus/pull/33), - # fallback to command without `--system` if the first one fails. - $(PYTHON) -m pip install --system --target=$(PIP_DIR) pip setuptools || \ - $(PYTHON) -m pip install --target=$(PIP_DIR) pip setuptools || \ - (echo "update of pip or setuptools failed, likely because pip is not installed (if you are on Debian/Ubuntu or derivative please install the python3-pip package)" >&2) - - # Install `meson` and `ninja` using `pip` into custom location, so we don't - # depend on system-wide installation. - $(PYTHON) -m pip install --upgrade --target=$(PIP_DIR) $(PIP_BUILD_BINARIES) meson==$(MESON_VERSION) ninja==$(NINJA_VERSION) +invoke: +ifeq ($(wildcard $(PIP_INVOKE_DIR)),) + # Install pip invoke into custom location, so we don't depend on system-wide + # installation. + $(PYTHON) -m pip install --upgrade --target="$(PIP_INVOKE_DIR)" invoke==$(INVOKE_VERSION) endif -setup: meson-ninja -# We try to call `--reconfigure` first as a workaround for this issue: -# https://github.com/ninja-build/ninja/issues/1997 -ifeq ($(MEDIASOUP_BUILDTYPE),Release) - $(MESON) setup \ - --prefix $(INSTALL_DIR) \ - --bindir "" \ - --libdir "" \ - --buildtype release \ - -Db_ndebug=true \ - -Db_pie=true \ - -Db_staticpic=true \ - --reconfigure \ - $(MESON_ARGS) \ - $(BUILD_DIR) || \ - $(MESON) setup \ - --prefix $(INSTALL_DIR) \ - --bindir "" \ - --libdir "" \ - --buildtype release \ - -Db_ndebug=true \ - -Db_pie=true \ - -Db_staticpic=true \ - $(MESON_ARGS) \ - $(BUILD_DIR) -else -ifeq ($(MEDIASOUP_BUILDTYPE),Debug) - $(MESON) setup \ - --prefix $(INSTALL_DIR) \ - --bindir "" \ - --libdir "" \ - --buildtype debug \ - -Db_pie=true \ - -Db_staticpic=true \ - --reconfigure \ - $(MESON_ARGS) \ - $(BUILD_DIR) || \ - $(MESON) setup \ - --prefix $(INSTALL_DIR) \ - --bindir "" \ - --libdir "" \ - --buildtype debug \ - -Db_pie=true \ - -Db_staticpic=true \ - $(MESON_ARGS) \ - $(BUILD_DIR) -else - $(MESON) setup \ - --prefix $(INSTALL_DIR) \ - --bindir "" \ - --libdir "" \ - --buildtype $(MEDIASOUP_BUILDTYPE) \ - -Db_ndebug=if-release \ - -Db_pie=true \ - -Db_staticpic=true \ - --reconfigure \ - $(MESON_ARGS) \ - $(BUILD_DIR) || \ - $(MESON) setup \ - --prefix $(INSTALL_DIR) \ - --bindir "" \ - --libdir "" \ - --buildtype $(MEDIASOUP_BUILDTYPE) \ - -Db_ndebug=if-release \ - -Db_pie=true \ - -Db_staticpic=true \ - $(MESON_ARGS) \ - $(BUILD_DIR) -endif -endif +meson-ninja: invoke + $(PYTHON) -m invoke meson-ninja -clean: - $(RM) -rf $(INSTALL_DIR) +setup: invoke + $(PYTHON) -m invoke setup -clean-build: - $(RM) -rf $(BUILD_DIR) +clean: invoke + $(PYTHON) -m invoke clean -clean-pip: - $(RM) -rf $(PIP_DIR) +clean-build: invoke + $(PYTHON) -m invoke clean-build -clean-subprojects: meson-ninja - $(MESON) subprojects purge --include-cache --confirm +clean-pip: invoke + $(PYTHON) -m invoke clean-pip -# Clean subprojects (ignore if it fails) and delete out dir. -clean-all: - -$(MESON) subprojects purge --include-cache --confirm - $(RM) -rf $(MEDIASOUP_OUT_DIR) +clean-subprojects: invoke + $(PYTHON) -m invoke clean-subprojects -# Update the wrap file of a subproject. Usage example: -# make update-wrap-file SUBPROJECT=openssl -update-wrap-file: meson-ninja - $(MESON) subprojects update --reset $(SUBPROJECT) +clean-all: invoke + $(PYTHON) -m invoke clean-all -mediasoup-worker: setup flatc -ifeq ($(MEDIASOUP_WORKER_BIN),) - $(MESON) compile -C $(BUILD_DIR) -j $(CORES) mediasoup-worker - $(MESON) install -C $(BUILD_DIR) --no-rebuild --tags mediasoup-worker -endif +# It requires the SUBPROJECT environment variable. +update-wrap-file: invoke + $(PYTHON) -m invoke subprojects $(SUBPROJECT) -libmediasoup-worker: setup flatc - $(MESON) compile -C $(BUILD_DIR) -j $(CORES) libmediasoup-worker - $(MESON) install -C $(BUILD_DIR) --no-rebuild --tags libmediasoup-worker +mediasoup-worker: invoke + $(PYTHON) -m invoke mediasoup-worker -flatc: setup - $(MESON) compile -C $(BUILD_DIR) flatbuffers-generator +libmediasoup-worker: invoke + $(PYTHON) -m invoke libmediasoup-worker -xcode: setup flatc - $(MESON) setup --buildtype debug --backend xcode $(MEDIASOUP_OUT_DIR)/xcode +flatc: invoke + $(PYTHON) -m invoke flatc -lint: - $(NPM) run lint --prefix scripts/ +xcode: invoke + $(PYTHON) -m invoke xcode -format: - $(NPM) run format --prefix scripts/ +lint: invoke + $(PYTHON) -m invoke lint -test: setup flatc - $(MESON) compile -C $(BUILD_DIR) -j $(CORES) mediasoup-worker-test - $(MESON) install -C $(BUILD_DIR) --no-rebuild --tags mediasoup-worker-test -ifeq ($(OS),Windows_NT) - # On Windows lcov doesn't work (at least not yet) and we need to add `.exe` to - # the binary path. - $(BUILD_DIR)/mediasoup-worker-test.exe --invisibles --use-colour=yes $(MEDIASOUP_TEST_TAGS) -else - $(LCOV) --directory ./ --zerocounters - $(BUILD_DIR)/mediasoup-worker-test --invisibles --use-colour=yes $(MEDIASOUP_TEST_TAGS) -endif +format: invoke + $(PYTHON) -m invoke format -test-asan: setup flatc - $(MESON) compile -C $(BUILD_DIR) -j $(CORES) mediasoup-worker-test-asan - $(MESON) install -C $(BUILD_DIR) --no-rebuild --tags mediasoup-worker-test-asan - ASAN_OPTIONS=detect_leaks=1 $(BUILD_DIR)/mediasoup-worker-test-asan --invisibles --use-colour=yes $(MEDIASOUP_TEST_TAGS) +test: invoke + $(PYTHON) -m invoke test -tidy: - $(PYTHON) ./scripts/clang-tidy.py \ - -clang-tidy-binary=./scripts/node_modules/.bin/clang-tidy \ - -clang-apply-replacements-binary=./scripts/node_modules/.bin/clang-apply-replacements \ - -header-filter="(Channel/**/*.hpp|DepLibSRTP.hpp|DepLibUV.hpp|DepLibWebRTC.hpp|DepOpenSSL.hpp|DepUsrSCTP.hpp|LogLevel.hpp|Logger.hpp|MediaSoupError.hpp|RTC/**/*.hpp|Settings.hpp|Utils.hpp|Worker.hpp|common.hpp|handles/**/*.hpp)" \ - -p=$(BUILD_DIR) \ - -j=$(CORES) \ - -checks=$(MEDIASOUP_TIDY_CHECKS) \ - -quiet +test-asan: invoke + $(PYTHON) -m invoke test-asan -fuzzer: setup flatc - $(MESON) compile -C $(BUILD_DIR) -j $(CORES) mediasoup-worker-fuzzer - $(MESON) install -C $(BUILD_DIR) --no-rebuild --tags mediasoup-worker-fuzzer +tidy: invoke + $(PYTHON) -m invoke tidy -fuzzer-run-all: - 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 +fuzzer: invoke + $(PYTHON) -m invoke fuzzer -docker: -ifeq ($(DOCKER_NO_CACHE),true) - $(DOCKER) build -f Dockerfile --no-cache --tag mediasoup/docker:latest . -else - $(DOCKER) build -f Dockerfile --tag mediasoup/docker:latest . -endif +fuzzer-run-all: invoke + $(PYTHON) -m invoke fuzzer-run-all -docker-run: - $(DOCKER) run \ - --name=mediasoupDocker -it --rm \ - --privileged \ - --cap-add SYS_PTRACE \ - -v $(shell pwd)/../:/mediasoup \ - mediasoup/docker:latest +docker: invoke + $(PYTHON) -m invoke docker -docker-alpine: -ifeq ($(DOCKER_NO_CACHE),true) - $(DOCKER) build -f Dockerfile.alpine --no-cache --tag mediasoup/docker-alpine:latest . -else - $(DOCKER) build -f Dockerfile.alpine --tag mediasoup/docker-alpine:latest . -endif +docker-run: invoke + $(PYTHON) -m invoke docker-run + +docker-alpine: invoke + $(PYTHON) -m invoke docker-alpine -docker-alpine-run: - $(DOCKER) run \ - --name=mediasoupDockerAlpine -it --rm \ - --privileged \ - --cap-add SYS_PTRACE \ - -v $(shell pwd)/../:/mediasoup \ - mediasoup/docker-alpine:latest +docker-alpine-run: invoke + $(PYTHON) -m invoke docker-alpine-run diff --git a/worker/build.rs b/worker/build.rs index 8bf0b836fe..06d25c0ce6 100644 --- a/worker/build.rs +++ b/worker/build.rs @@ -7,8 +7,8 @@ fn main() { return; } - // On Windows Rust always links against release version of MSVC runtime, thus requires Release - // build here. + // On Windows Rust always links against release version of MSVC runtime, thus requires + // Release build here let build_type = if cfg!(all(debug_assertions, not(windows))) { "Debug" } else { @@ -45,15 +45,9 @@ fn main() { ) .expect("Failed to write generated Rust flatbuffers into fbs.rs"); - // Force forward slashes on Windows too so that is plays well with our dumb `Makefile`. + // Force forward slashes on Windows too so that is plays well with our tasks.py let mediasoup_out_dir = format!("{}/out", out_dir.replace('\\', "/")); - // Store original PATH so we make `make clean-all` use it. This is because, in Windows, - // we may need to fetch `make` and we store it in out/msys and then we add out/msys/bin - // to the PATH, and that folder may contain rm.exe, so `make clean-all` would use - // that rm.exe and delete itself and make the task fail. - let original_path = env::var("PATH").unwrap(); - // Add C++ std lib #[cfg(target_os = "linux")] { @@ -110,62 +104,49 @@ fn main() { println!("cargo:rustc-link-lib=dylib=c++"); println!("cargo:rustc-link-lib=dylib=c++abi"); } - #[cfg(target_os = "windows")] - { - if !std::path::Path::new("worker/out/msys/bin/make.exe").exists() { - let python = if let Ok(python) = env::var("PYTHON") { - python - } else if Command::new("where") - .arg("python3") - .status() - .expect("Failed to start") - .success() - { - "python3".to_string() - } else { - "python".to_string() - }; - - let dir = format!("{}/msys", mediasoup_out_dir.replace('\\', "/")); - - if !Command::new(python) - .arg("scripts\\getmake.py") - .arg("--dir") - .arg(dir.clone()) - .status() - .expect("Failed to start") - .success() - { - panic!("Failed to install MSYS/make") - } - - env::set_var( - "PATH", - format!("{}\\bin;{}", dir, env::var("PATH").unwrap()), - ); - } - env::set_var( - "PATH", - format!( - "{}\\worker\\out\\msys\\bin;{}", - env::current_dir() - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - env::var("PATH").unwrap() - ), - ); + // Install Python invoke package in custom folder + let pip_invoke_dir = format!("{out_dir}/pip_invoke"); + let invoke_version = "2.2.0"; + let python = env::var("PYTHON").unwrap_or("python3".to_string()); + let mut pythonpath = if env::var("PYTHONPATH").is_ok() { + let original_pythonpath = env::var("PYTHONPATH").unwrap(); + format!("{pip_invoke_dir}:{original_pythonpath}") + } else { + pip_invoke_dir.clone() + }; + + // Force ";" in PYTHONPATH on Windows + if cfg!(target_os = "windows") { + pythonpath = pythonpath.replace(':', ";"); + } + + if !Command::new(&python) + .arg("-m") + .arg("pip") + .arg("install") + .arg("--upgrade") + .arg(format!("--target={pip_invoke_dir}")) + .arg(format!("invoke=={invoke_version}")) + .spawn() + .expect("Failed to start") + .wait() + .expect("Wasn't running") + .success() + { + panic!("Failed to install Python invoke package") } // Build - if !Command::new("make") + if !Command::new(&python) + .arg("-m") + .arg("invoke") .arg("libmediasoup-worker") + .env("PYTHONPATH", &pythonpath) .env("MEDIASOUP_OUT_DIR", &mediasoup_out_dir) .env("MEDIASOUP_BUILDTYPE", build_type) // Force forward slashes on Windows too, otherwise Meson thinks path is not absolute 🤷 - .env("INSTALL_DIR", &out_dir.replace('\\', "/")) + .env("MEDIASOUP_INSTALL_DIR", &out_dir.replace('\\', "/")) .spawn() .expect("Failed to start") .wait() @@ -205,9 +186,11 @@ fn main() { if env::var("KEEP_BUILD_ARTIFACTS") != Ok("1".to_string()) { // Clean - if !Command::new("make") + if !Command::new(python) + .arg("-m") + .arg("invoke") .arg("clean-all") - .env("PATH", original_path) + .env("PYTHONPATH", &pythonpath) .env("MEDIASOUP_OUT_DIR", &mediasoup_out_dir) .spawn() .expect("Failed to start") diff --git a/worker/scripts/clang-format.mjs b/worker/scripts/clang-format.mjs index a7a0f00f4d..13fbd0aaa9 100644 --- a/worker/scripts/clang-format.mjs +++ b/worker/scripts/clang-format.mjs @@ -25,7 +25,7 @@ async function run() case 'lint': { executeCmd( - `${clangFormatNativeBinary} --Werror --dry-run ${workerFiles.join(' ')}` + `"${clangFormatNativeBinary}" --Werror --dry-run ${workerFiles.join(' ')}` ); break; @@ -34,7 +34,7 @@ async function run() case 'format': { executeCmd( - `${clangFormatNativeBinary} --Werror -i ${workerFiles.join(' ')}` + `"${clangFormatNativeBinary}" --Werror -i ${workerFiles.join(' ')}` ); break; diff --git a/worker/scripts/cpu_cores.sh b/worker/scripts/cpu_cores.sh deleted file mode 100755 index b5cdde81d0..0000000000 --- a/worker/scripts/cpu_cores.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env sh - -set -e - -OS="$(uname -s)" -NUM_CORES= - -case "${OS}" in - Linux*) NUM_CORES=$(nproc);; - Darwin*|FreeBSD) NUM_CORES=$(sysctl -n hw.ncpu);; - MINGW*) NUM_CORES=$NUMBER_OF_PROCESSORS;; - *) NUM_CORES=1;; -esac - -if [ -n "${MEDIASOUP_MAX_CORES}" ]; then - NUM_CORES=$((MEDIASOUP_MAX_CORES>NUM_CORES ? NUM_CORES : MEDIASOUP_MAX_CORES)) -fi - -echo ${NUM_CORES} diff --git a/worker/scripts/get-dep.sh b/worker/scripts/get-dep.sh index d538ae8606..5d6fced81f 100755 --- a/worker/scripts/get-dep.sh +++ b/worker/scripts/get-dep.sh @@ -7,7 +7,7 @@ DEP=$1 current_dir_name=${WORKER_PWD##*/} if [ "${current_dir_name}" != "worker" ] ; then - echo ">>> [ERROR] $(basename $0) must be called from mediasoup/worker/ directory" >&2 + echo ">>> [ERROR] $(basename $0) must be called from mediasoup/worker directory" >&2 exit 1 fi diff --git a/worker/scripts/getmake.py b/worker/scripts/getmake.py deleted file mode 100644 index 50ec9ea7f1..0000000000 --- a/worker/scripts/getmake.py +++ /dev/null @@ -1,28 +0,0 @@ -import argparse, io, hashlib, tarfile, urllib.request - -argParser = argparse.ArgumentParser() - -argParser.add_argument( - '--dir', - type=str, - required=True, - help='absolute path of the directoy in which fetched content will be placed' -) - -args = argParser.parse_args() - -def get(url, digest): - data = urllib.request.urlopen(url).read() - assert hashlib.sha256(data).hexdigest() == digest - tar = tarfile.open(fileobj=io.BytesIO(data)) - tar.extractall(args.dir) - tar.close() - -get('https://sourceforge.net/projects/mingw/files/MSYS/Base/msys-core/msys-1.0.19-1/msysCORE-1.0.19-1-msys-1.0.19-bin.tar.xz/download', '8c4157d739a460f85563bc4451e9f1bbd42b13c4f63770d43b9f45a781f07858') -get('https://sourceforge.net/projects/mingw/files/MSYS/Base/libiconv/libiconv-1.14-1/libiconv-1.14-1-msys-1.0.17-dll-2.tar.lzma/download', '196921e8c232259c8e6a6852b9ee8d9ab2d29a91419f0c8dc27ba6f034231683') -get('https://sourceforge.net/projects/mingw/files/MSYS/Base/gettext/gettext-0.18.1.1-1/libintl-0.18.1.1-1-msys-1.0.17-dll-8.tar.lzma/download', '29db8c969661c511fbe2a341ab25c993c5f9c555842a75d6ddbcfa70dec16910') -get('https://sourceforge.net/projects/mingw/files/MSYS/Base/coreutils/coreutils-5.97-3/coreutils-5.97-3-msys-1.0.13-bin.tar.lzma/download', 'f8c7990416ea16a74ac336dcfe0f596bc46b8724b2d58cf8a3509414220b2366') -get('https://sourceforge.net/projects/mingw/files/MSYS/Base/regex/regex-1.20090805-2/libregex-1.20090805-2-msys-1.0.13-dll-1.tar.lzma/download', '85dd8c1e27a90675c5f867be57ba7ae2bb55dde8cd2d19f284c896be134bd3d1') -get('https://sourceforge.net/projects/mingw/files/MSYS/Base/termcap/termcap-0.20050421_1-2/libtermcap-0.20050421_1-2-msys-1.0.13-dll-0.tar.lzma/download', '62b58fe0880f0972fcc84a819265989b02439c1c5185870227bd25f870f7adb6') -get('https://sourceforge.net/projects/mingw/files/MSYS/Base/bash/bash-3.1.23-1/bash-3.1.23-1-msys-1.0.18-bin.tar.xz/download', '38da5419969ab883058a96322bb0f51434dd4e9f71de09cd4f75b96750944533') -get('https://sourceforge.net/projects/mingw/files/MSYS/Base/make/make-3.81-3/make-3.81-3-msys-1.0.13-bin.tar.lzma/download', '847f0cbbf07135801c8e67bf692d29b1821e816ad828753c997fa869a9b89988') diff --git a/worker/tasks.py b/worker/tasks.py new file mode 100644 index 0000000000..6e471b2989 --- /dev/null +++ b/worker/tasks.py @@ -0,0 +1,602 @@ +# Ignore these pylint warnings, concentions and refactoring messages: +# - W0301: Unnecessary semicolon (unnecessary-semicolon) +# - W0622: Redefining built-in 'format' (redefined-builtin) +# - W0702: No exception type(s) specified (bare-except) +# - C0114: Missing module docstring (missing-module-docstring) +# - C0301: Line too long (line-too-long) +# +# pylint: disable=W0301,W0622,W0702,C0114,C0301 + +# +# This is a tasks.py file for the pip invoke package: https://docs.pyinvoke.org/. +# +# It's a replacement of our Makefile with same tasks. +# +# Usage: +# invoke --list +# + +import sys; +import os; +import inspect; +import shutil; +# We import this from a custom location and pylint doesn't know. +from invoke import task; # pylint: disable=import-error + +MEDIASOUP_BUILDTYPE = os.getenv('MEDIASOUP_BUILDTYPE') or 'Release'; +WORKER_DIR = os.path.dirname(os.path.abspath( + inspect.getframeinfo(inspect.currentframe()).filename +)); +# NOTE: MEDIASOUP_OUT_DIR is overrided by build.rs. +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'; +# Custom pip folder for invoke package. +PIP_INVOKE_DIR = f'{MEDIASOUP_OUT_DIR}/pip_invoke'; +# Custom pip folder for meson and ninja packages. +PIP_MESON_NINJA_DIR = f'{MEDIASOUP_OUT_DIR}/pip_meson_ninja'; +# Custom pip folder for pylint package. +# NOTE: We do this because using --target and --upgrade in `pip install` is not +# supported and a latter invokation entirely replaces the bin folder. +PIP_PYLINT_DIR = f'{MEDIASOUP_OUT_DIR}/pip_pylint'; +# If available (only on some *nix systems), os.sched_getaffinity(0) gets set of +# CPUs the calling thread is restricted to. Instead, os.cpu_count() returns the +# total number of CPUs in a system (it doesn't take into account how many of them +# the calling thread can use). +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.2.1'; +# MESON_ARGS can be used to provide extra configuration parameters to meson, +# such as adding defines or changing optimization options. For instance, use +# `MESON_ARGS="-Dms_log_trace=true -Dms_log_file_line=true" npm i` to compile +# worker with tracing and enabled. +# NOTE: On Windows make sure to add `--vsenv` or have MSVS environment already +# active if you override this parameter. +MESON_ARGS = os.getenv('MESON_ARGS') if os.getenv('MESON_ARGS') else '--vsenv' if os.name == 'nt' else ''; +# Let's use a specific version of ninja to avoid buggy version 1.11.1: +# https://mediasoup.discourse.group/t/partly-solved-could-not-detect-ninja-v1-8-2-or-newer/ +# https://github.com/ninja-build/ninja/issues/2211 +# https://github.com/ninja-build/ninja/issues/2212 +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'; +LCOV = f'{WORKER_DIR}/deps/lcov/bin/lcov'; +DOCKER = os.getenv('DOCKER') or 'docker'; +# pty=True in ctx.run() is not available on Windows so if stdout is not a TTY +# let's assume PTY is not supported. Related issue in invoke project: +# https://github.com/pyinvoke/invoke/issues/561 +PTY_SUPPORTED = sys.stdout.isatty(); +# Use sh (widely supported, more than bash) if not in Windows. +SHELL = '/bin/sh' if not os.name == 'nt' else None; + +# Disable `*.pyc` files creation. +os.environ['PYTHONDONTWRITEBYTECODE'] = 'true'; + +# Instruct meson where to look for ninja binary. +if os.name == 'nt': + # Windows is, of course, special. + os.environ['NINJA'] = f'{PIP_MESON_NINJA_DIR}/bin/ninja.exe'; +else: + os.environ['NINJA'] = f'{PIP_MESON_NINJA_DIR}/bin/ninja'; + +# Instruct Python where to look for modules it needs, such that meson actually +# runs from installed location. +# NOTE: On Windows we must use ; instead of : to separate paths. +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}'; + + +@task +def meson_ninja(ctx): + """ + Install meson and ninja (also update Python pip and setuptools packages) + """ + if os.path.isfile(MESON): + return; + + # Updated pip and setuptools are needed for meson. + # `--system` is not present everywhere and is only needed as workaround for + # Debian-specific issue (copied from https://github.com/gluster/gstatus/pull/33), + # fallback to command without `--system` if the first one fails. + try: + ctx.run( + f'"{PYTHON}" -m pip install --system --upgrade --target="{PIP_MESON_NINJA_DIR}" pip setuptools', + echo=True, + hide=True, + shell=SHELL + ); + except: + ctx.run( + f'"{PYTHON}" -m pip install --upgrade --target="{PIP_MESON_NINJA_DIR}" pip setuptools', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + # Workaround for NixOS and Guix that don't work with pre-built binaries, see: + # https://github.com/NixOS/nixpkgs/issues/142383. + pip_build_binaries = '--no-binary :all:' if os.path.isfile('/etc/NIXOS') or os.path.isdir('/etc/guix') else ''; + + # Install meson and ninja using pip into our custom location, so we don't + # depend on system-wide installation. + ctx.run( + f'"{PYTHON}" -m pip install --upgrade --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): + """ + Run meson setup + """ + # We add --reconfigure first as a workaround for this issue: + # https://github.com/ninja-build/ninja/issues/1997 + if MEDIASOUP_BUILDTYPE == 'Release': + try: + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype release -Db_ndebug=true -Db_pie=true -Db_staticpic=true --reconfigure {MESON_ARGS} "{BUILD_DIR}"', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + except: + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype release -Db_ndebug=true -Db_pie=true -Db_staticpic=true {MESON_ARGS} "{BUILD_DIR}"', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + elif MEDIASOUP_BUILDTYPE == 'Debug': + try: + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype debug -Db_pie=true -Db_staticpic=true --reconfigure {MESON_ARGS} "{BUILD_DIR}"', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + except: + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype debug -Db_pie=true -Db_staticpic=true {MESON_ARGS} "{BUILD_DIR}"', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + else: + try: + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype {MEDIASOUP_BUILDTYPE} -Db_ndebug=if-release -Db_pie=true -Db_staticpic=true --reconfigure {MESON_ARGS} "{BUILD_DIR}"', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + except: + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" setup --prefix "{MEDIASOUP_INSTALL_DIR}" --bindir "" --libdir "" --buildtype {MEDIASOUP_BUILDTYPE} -Db_ndebug=if-release -Db_pie=true -Db_staticpic=true {MESON_ARGS} "{BUILD_DIR}"', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task +def clean(ctx): # pylint: disable=unused-argument + """ + Clean the installation directory + """ + try: + shutil.rmtree(MEDIASOUP_INSTALL_DIR); + except: + pass; + + +@task +def clean_build(ctx): # pylint: disable=unused-argument + """ + Clean the build directory + """ + try: + shutil.rmtree(BUILD_DIR); + except: + pass; + + +@task +def clean_pip(ctx): # pylint: disable=unused-argument + """ + Clean the local pip setup + """ + try: + shutil.rmtree(PIP_MESON_NINJA_DIR); + except: + pass; + + try: + shutil.rmtree(PIP_PYLINT_DIR); + except: + pass; + + +@task(pre=[meson_ninja]) +def clean_subprojects(ctx): + """ + Clean meson subprojects + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" subprojects purge --include-cache --confirm', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task +def clean_all(ctx): + """ + Clean meson subprojects and all installed/built artificats + """ + with ctx.cd(WORKER_DIR): + try: + ctx.run( + f'"{MESON}" subprojects purge --include-cache --confirm', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + except: + pass; + + try: + shutil.rmtree(MEDIASOUP_OUT_DIR); + except: + pass; + + +@task(pre=[meson_ninja]) +def update_wrap_file(ctx, subproject): + """ + Update the wrap file of a subproject + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" subprojects update --reset {subproject}', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task(pre=[setup]) +def flatc(ctx): + """ + Compile FlatBuffers FBS files + """ + with ctx.cd(WORKER_DIR): + 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): + """ + Compile mediasoup-worker binary + """ + if os.getenv('MEDIASOUP_WORKER_BIN'): + print('skipping mediasoup-worker compilation due to the existence of the MEDIASOUP_WORKER_BIN environment variable'); + return; + + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + with ctx.cd(WORKER_DIR): + 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): + """ + Compile libmediasoup-worker library + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} libmediasoup-worker', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + with ctx.cd(WORKER_DIR): + 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): + """ + Setup Xcode project + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" setup --buildtype {MEDIASOUP_BUILDTYPE} --backend xcode "{MEDIASOUP_OUT_DIR}/xcode"', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task +def lint(ctx): + """ + Lint source code + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{NPM}" run lint --prefix scripts/', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + if not os.path.isdir(PIP_PYLINT_DIR): + # Install pylint using pip into our custom location. + ctx.run( + f'"{PYTHON}" -m pip install --upgrade --target="{PIP_PYLINT_DIR}" pylint=={PYLINT_VERSION}', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{PYTHON}" -m pylint tasks.py', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task +def format(ctx): + """ + Format source code according to lint rules + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{NPM}" run format --prefix scripts/', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task(pre=[setup, flatc]) +def test(ctx): + """ + Run worker tests + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker-test', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" install -C "{BUILD_DIR}" --no-rebuild --tags mediasoup-worker-test', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + mediasoup_test_tags = os.getenv('MEDIASOUP_TEST_TAGS') or ''; + + # On Windows lcov doesn't work (at least not yet) and we need to add .exe to + # the binary path. + if os.name == 'nt': + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{BUILD_DIR}/mediasoup-worker-test.exe" --invisibles --use-colour=yes {mediasoup_test_tags}', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + else: + ctx.run( + f'"{LCOV}" --directory "{WORKER_DIR}" --zerocounters', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{BUILD_DIR}/mediasoup-worker-test" --invisibles --use-colour=yes {mediasoup_test_tags}', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task(pre=[setup, flatc]) +def test_asan(ctx): + """ + Run worker test with Address Sanitizer + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker-test-asan', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" install -C "{BUILD_DIR}" --no-rebuild --tags mediasoup-worker-test-asan', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + mediasoup_test_tags = os.getenv('MEDIASOUP_TEST_TAGS') or ''; + + with ctx.cd(WORKER_DIR): + ctx.run( + f'ASAN_OPTIONS=detect_leaks=1 "{BUILD_DIR}/mediasoup-worker-test-asan" --invisibles --use-colour=yes {mediasoup_test_tags}', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task +def tidy(ctx): + """ + Perform C++ checks with clang-tidy + """ + mediasoup_tidy_checks = os.getenv('MEDIASOUP_TIDY_CHECKS') or ''; + + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{PYTHON}" ./scripts/clang-tidy.py -clang-tidy-binary=./scripts/node_modules/.bin/clang-tidy -clang-apply-replacements-binary=./scripts/node_modules/.bin/clang-apply-replacements -header-filter="(Channel/**/*.hpp|DepLibSRTP.hpp|DepLibUV.hpp|DepLibWebRTC.hpp|DepOpenSSL.hpp|DepUsrSCTP.hpp|LogLevel.hpp|Logger.hpp|MediaSoupError.hpp|RTC/**/*.hpp|Settings.hpp|Utils.hpp|Worker.hpp|common.hpp|handles/**/*.hpp)" -p="{BUILD_DIR}" -j={NUM_CORES} -checks={mediasoup_tidy_checks} -quiet', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task(pre=[setup, flatc]) +def fuzzer(ctx): + """ + Build the mediasoup-worker-fuzzer binary (which uses libFuzzer) + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{MESON}" compile -C "{BUILD_DIR}" -j {NUM_CORES} mediasoup-worker-fuzzer', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + with ctx.cd(WORKER_DIR): + 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): + """ + Run all fuzzer cases + """ + with ctx.cd(WORKER_DIR): + 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): + """ + Build a Linux Ubuntu Docker image with fuzzer capable clang++ + """ + if os.getenv('DOCKER_NO_CACHE') == 'true': + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{DOCKER}" build -f Dockerfile --no-cache --tag mediasoup/docker:latest .', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + else: + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{DOCKER}" build -f Dockerfile --tag mediasoup/docker:latest .', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + + +@task +def docker_run(ctx): + """ + Run a container of the Ubuntu Docker image created in the docker task + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{DOCKER}" run --name=mediasoupDocker -it --rm --privileged --cap-add SYS_PTRACE -v "{WORKER_DIR}/../:/mediasoup" mediasoup/docker:latest', + echo=True, + pty=True, # NOTE: Needed to enter the terminal of the Docker image. + shell=SHELL + ); + + +@task +def docker_alpine(ctx): + """ + Build a Linux Alpine Docker image + """ + if os.getenv('DOCKER_NO_CACHE') == 'true': + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{DOCKER}" build -f Dockerfile.alpine --no-cache --tag mediasoup/docker-alpine:latest .', + echo=True, + pty=PTY_SUPPORTED, + shell=SHELL + ); + else: + with ctx.cd(WORKER_DIR): + 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): + """ + Run a container of the Alpine Docker image created in the docker_alpine task + """ + with ctx.cd(WORKER_DIR): + ctx.run( + f'"{DOCKER}" run --name=mediasoupDockerAlpine -it --rm --privileged --cap-add SYS_PTRACE -v "{WORKER_DIR}/../:/mediasoup" mediasoup/docker-alpine:latest', + echo=True, + pty=True, # NOTE: Needed to enter the terminal of the Docker image. + shell=SHELL + );