From 05bf8a52dc6035a5d71378b6a6d2526128a05d02 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 13 Oct 2025 14:39:37 -0500 Subject: [PATCH 1/3] test GitHub Actions workflow --- .github/workflows/webrtc-builds.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/webrtc-builds.yml b/.github/workflows/webrtc-builds.yml index b6c97b1f6..dce371b17 100644 --- a/.github/workflows/webrtc-builds.yml +++ b/.github/workflows/webrtc-builds.yml @@ -15,8 +15,8 @@ name: WebRTC builds on: push: - tags: - - "webrtc-*" +# tags: +# - "webrtc-*" workflow_dispatch: jobs: From 4c69fbf922da2ef689ec4a7716a36779d7dade3b Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 8 Oct 2025 16:54:31 -0500 Subject: [PATCH 2/3] build_linux.sh: add option to use host toolchain This adds a --toolchain CLI option to the script that can be either * gnu * llvm * chromium-llvm The gnu and llvm options use the system's toolchain. chromium-llvm uses Chromium's prebuilt LLVM toolchain with its Debian sysroot. Previously, the script tried to combine the system's gcc with libstdc++ from Chromium's Debian sysroot, which failed to find glibc headers: [1/4184] CXX obj/third_party/abseil-cpp/absl/base/base/cycleclock.o FAILED: obj/third_party/abseil-cpp/absl/base/base/cycleclock.o g++ -MMD -MF obj/third_party/abseil-cpp/absl/base/base/cycleclock.o.d -DUSE_UDEV -DUSE_AURA=1 -DUSE_GLIB=1 -DUSE_OZONE=1 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -D_GLIBCXX_ASSERTIONS=1 -DCR_SYSROOT_KEY=20250129T203412Z-1 -DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -DABSL_ALLOCATOR_NOTHROW=1 -I.. -Igen -I../third_party/abseil-cpp -fno-strict-overflow -fno-ident -fno-strict-aliasing -fstack-protector -funwind-tables -fPIC -pipe -pthread -m64 -msse3 -Wno-builtin-macro-redefined -D__DATE__= -D__TIME__= -D__TIMESTAMP__= -O2 -fdata-sections -ffunction-sections -fno-math-errno -fno-omit-frame-pointer -g0 -fvisibility=hidden -Wno-unused-local-typedefs -Wno-maybe-uninitialized -Wno-deprecated-declarations -Wno-comments -Wno-packed-not-aligned -Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi -std=gnu++2a -Wno-changes-meaning -fno-exceptions --sysroot=../build/linux/debian_bullseye_amd64-sysroot -fvisibility-inlines-hidden -Wno-narrowing -Wno-class-memaccess -Wno-invalid-offsetof -c ../third_party/abseil-cpp/absl/base/internal/cycleclock.cc -o obj/third_party/abseil-cpp/absl/base/base/cycleclock.o In file included from /usr/include/c++/15/bits/version.h:51, from /usr/include/c++/15/atomic:50, from ../third_party/abseil-cpp/absl/base/internal/cycleclock.h:45, from ../third_party/abseil-cpp/absl/base/internal/cycleclock.cc:23: /usr/include/c++/15/x86_64-redhat-linux/bits/c++config.h:3:10: fatal error: bits/wordsize.h: No such file or directory 3 | #include | ^~~~~~~~~~~~~~~~~ --- .github/workflows/webrtc-builds.yml | 34 +- webrtc-sys/Cargo.toml | 7 +- webrtc-sys/build.rs | 17 + webrtc-sys/libwebrtc/build_linux.sh | 111 +++- webrtc-sys/src/cxx.cc | 870 ++++++++++++++++++++++++++++ 5 files changed, 980 insertions(+), 59 deletions(-) create mode 100644 webrtc-sys/src/cxx.cc diff --git a/.github/workflows/webrtc-builds.yml b/.github/workflows/webrtc-builds.yml index dce371b17..bb1db314c 100644 --- a/.github/workflows/webrtc-builds.yml +++ b/.github/workflows/webrtc-builds.yml @@ -47,12 +47,12 @@ jobs: - name: linux os: ubuntu-latest - cmd: ./build_linux.sh + cmd: ./build_linux.sh --toolchain chromium-llvm arch: x64 - name: linux os: ubuntu-latest - cmd: ./build_linux.sh + cmd: ./build_linux.sh --toolchain chromium-llvm arch: arm64 - name: android @@ -110,38 +110,18 @@ jobs: - name: install setuptools (none-macOS) if: ${{ matrix.target.os != 'macos-latest' }} run: | - pip3 install setuptools # pkg_resources is sometimes not found? + pip3 install setuptools # pkg_resources is sometimes not found? - - name: Add GCC PPA and install GCC 14 - if: ${{ matrix.target.os == 'ubuntu-latest' }} - run: | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt install gcc-14 g++-14 g++-14-aarch64-linux-gnu -y - - - name: Verify GCC 14 installation - if: ${{ matrix.target.os == 'ubuntu-latest' }} - run: | - gcc-14 --version - g++-14 --version - aarch64-linux-gnu-g++-14 --version - - - name: Set GCC 14 as default (optional) - if: ${{ matrix.target.os == 'ubuntu-latest' }} - run: | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 140 --slave /usr/bin/g++ g++ /usr/bin/g++-14 - sudo update-alternatives --config gcc - gcc --version - sudo update-alternatives --install /usr/bin/aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc /usr/bin/aarch64-linux-gnu-gcc-14 140 --slave /usr/bin/aarch64-linux-gnu-g++ aarch64-linux-gnu-g++ /usr/bin/aarch64-linux-gnu-g++-14 - sudo update-alternatives --config aarch64-linux-gnu-gcc - aarch64-linux-gnu-gcc --version - - name: Install linux dependencies if: ${{ matrix.target.os == 'ubuntu-latest' }} run: | sudo apt update -y sudo apt install -y ninja-build pkg-config openjdk-11-jdk + - name: Disable __GLIBC_USE_ISOC2X macro + if: ${{ matrix.target.name == 'linux' && matrix.target.arch == 'arm64' }} + run: sudo sed -i 's/__GLIBC_USE_ISOC2X[[:space:]]*1/__GLIBC_USE_ISOC2X\t0/' /usr/include/features.h + - name: Install macos dependencies if: ${{ matrix.target.os == 'macos-latest' }} run: brew install ninja diff --git a/webrtc-sys/Cargo.toml b/webrtc-sys/Cargo.toml index f34c155ab..1d23d99a7 100644 --- a/webrtc-sys/Cargo.toml +++ b/webrtc-sys/Cargo.toml @@ -13,12 +13,15 @@ use_vaapi = [] use_nvidia = [] [dependencies] -cxx = "1.0" +# Copy an updated version of the cxx.cc file from cxx's repository +# into the src directory when updating to a new version of cxx +# and change the include path for cxx.h to "rust/cxx.h" +cxx = "=1.0.186" log = "0.4" [build-dependencies] webrtc-sys-build = { workspace = true } -cxx-build = "1.0" +cxx-build = "=1.0.186" glob = "0.3" cc = "1.0" diff --git a/webrtc-sys/build.rs b/webrtc-sys/build.rs index 0f5d4d2bd..a907168d3 100644 --- a/webrtc-sys/build.rs +++ b/webrtc-sys/build.rs @@ -148,6 +148,23 @@ fn main() { .flag("/EHsc"); } "linux" => { + // If libwebrtc was built with Chromium's libc++, the C++ in this crate needs to be built with it too. + let buildtools = webrtc_include.join("buildtools/third_party/libc++"); + if buildtools.exists() { + // Chromium's libc++ doesn't build with GCC + if env::var("CC").is_err() { + builder.compiler("clang++"); + } + builder.include(buildtools); + builder.flag("-nostdinc++"); + let webrtc_include = webrtc_include.to_string_lossy(); + builder.flag(format!("-isystem{webrtc_include}/third_party/libc++/src/include")); + builder.flag(format!("-isystem{webrtc_include}/third_party/libc++abi/src/include")); + // The cxx crate builds this C++ file. However, this crate needs to rebuild it when using a + // different C++ standard library or linking will fail with unresolved symbol errors. + builder.file("src/cxx.cc"); + } + println!("cargo:rustc-link-lib=dylib=rt"); println!("cargo:rustc-link-lib=dylib=dl"); println!("cargo:rustc-link-lib=dylib=pthread"); diff --git a/webrtc-sys/libwebrtc/build_linux.sh b/webrtc-sys/libwebrtc/build_linux.sh index a1d84fe1f..82a5a7eb9 100755 --- a/webrtc-sys/libwebrtc/build_linux.sh +++ b/webrtc-sys/libwebrtc/build_linux.sh @@ -16,6 +16,7 @@ arch="" profile="release" +toolchain="gnu" while [ "$#" -gt 0 ]; do case "$1" in @@ -35,6 +36,14 @@ while [ "$#" -gt 0 ]; do fi shift 2 ;; + --toolchain) + toolchain="$2" + if [ "$toolchain" != "gnu" ] && [ "$toolchain" != "llvm" ] && [ "$toolchain" != "chromium-llvm" ]; then + echo "Error: Invalid value for --toolchain. Must be 'gnu', 'llvm', or 'chromium-llvm' (Chromium's bundled Clang with Debian sysroot)" + exit 1 + fi + shift 2 + ;; *) echo "Error: Unknown argument '$1'" exit 1 @@ -50,46 +59,78 @@ fi echo "Building LiveKit WebRTC - Linux" echo "Arch: $arch" echo "Profile: $profile" +echo "Toolchain: $toolchain" + +export COMMAND_DIR=$(cd $(dirname $0); pwd) +export OUTPUT_DIR="$(pwd)/build-$arch-$profile" +export ARTIFACTS_DIR="$(pwd)/linux-$arch-$profile" + +if [ "$toolchain" == "gnu" ]; then + [ -n "$CC" ] || export CC="$(which gcc)" + [ -n "$CXX" ] || export CXX="$(which g++)" + [ -n "$AR" ] || export AR="$(which ar)" + [ -n "$NM" ] || export NM="$(which nm)" + export CXXFLAGS="${CXXFLAGS} -Wno-changes-meaning -Wno-unknown-pragmas -D_DEFAULT_SOURCE" + OBJCOPY="$(which objcopy)" + chromium_libcxx=false + toolchain_gn_args="is_clang=false \ + use_sysroot=false \ + custom_toolchain=\"//build/toolchain/linux/unbundle:default\" \ + host_toolchain=\"//build/toolchain/linux/unbundle:default\"" +elif [ "$toolchain" == "llvm" ]; then + [ -n "$CC" ] || export CC="$(which clang)" + [ -n "$CXX" ] || export CXX="$(which clang++)" + [ -n "$AR" ] || export AR="$(which llvm-ar)" + [ -n "$NM" ] || export NM="$(which llvm-nm)" + OBJCOPY="$(which llvm-objcopy)" + # Using system libc++ stumbles over + # https://github.com/llvm/llvm-project/issues/50248 + # so use Chromium's libc++ + chromium_libcxx=true + toolchain_gn_args="is_clang=true \ + clang_use_chrome_plugins=false \ + use_sysroot=false \ + custom_toolchain=\"//build/toolchain/linux/unbundle:default\" \ + host_toolchain=\"//build/toolchain/linux/unbundle:default\"" +elif [ "$toolchain" == "chromium-llvm" ]; then + AR="$COMMAND_DIR/src/third_party/llvm-build/Release+Asserts/bin/llvm-ar" + OBJCOPY="$COMMAND_DIR/src/third_party/llvm-build/Release+Asserts/bin/llvm-objcopy" + chromium_libcxx=true + toolchain_gn_args="is_clang=true \ + use_custom_libcxx=true \ + use_sysroot=true" +fi + +set -x if [ ! -e "$(pwd)/depot_tools" ] then git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git fi -export COMMAND_DIR=$(cd $(dirname $0); pwd) +# must be done after runing `which` to find toolchain's executables above export PATH="$(pwd)/depot_tools:$PATH" -export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile" -export ARTIFACTS_DIR="$(pwd)/linux-$arch-$profile" -if [ ! -e "$(pwd)/src" ] -then - gclient sync -D --no-history +if [ ! -e "$(pwd)/src" ]; then + # use --nohooks to avoid the download_from_google_storage hook that takes > 6 minutes + # then manually run the other hooks + gclient sync -D --no-history --nohooks + python3 src/tools/rust/update_rust.py + if [ "$toolchain" == "chromium-llvm" ] || [ "$toolchain" == "llvm" ]; then + python3 src/tools/clang/scripts/update.py + fi + if [ "$toolchain" == "chromium-llvm" ]; then + python3 src/build/linux/sysroot_scripts/install-sysroot.py --arch=x64 + python3 src/build/linux/sysroot_scripts/install-sysroot.py --arch=arm64 + fi fi cd src git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn - -cd build - -git apply "$COMMAND_DIR/patches/force_gcc.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn - -cd .. - -cd third_party - git apply "$COMMAND_DIR/patches/david_disable_gun_source_macro.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn - -cd ../.. - -mkdir -p "$ARTIFACTS_DIR/lib" - -python3 "./src/build/linux/sysroot_scripts/install-sysroot.py" --arch="$arch" - -if [ "$arch" = "arm64" ]; then - sudo sed -i 's/__GLIBC_USE_ISOC2X[[:space:]]*1/__GLIBC_USE_ISOC2X\t0/' /usr/aarch64-linux-gnu/include/features.h -fi +cd .. debug="false" if [ "$profile" = "debug" ]; then @@ -101,7 +142,7 @@ args="is_debug=$debug \ target_cpu=\"$arch\" \ rtc_enable_protobuf=false \ treat_warnings_as_errors=false \ - use_custom_libcxx=false \ + use_custom_libcxx=${chromium_libcxx} use_llvm_libatomic=false \ use_libcxx_modules=false \ use_custom_libcxx_for_host=false \ @@ -119,8 +160,10 @@ args="is_debug=$debug \ symbol_level=0 \ enable_iterator_debugging=false \ use_rtti=true \ - is_clang=false \ - rtc_use_x11=false" + rtc_use_x11=false \ + $toolchain_gn_args" + +set -e # generate ninja files gn gen "$OUTPUT_DIR" --root="src" --args="${args}" @@ -128,10 +171,12 @@ gn gen "$OUTPUT_DIR" --root="src" --args="${args}" # build static library ninja -C "$OUTPUT_DIR" :default +mkdir -p "$ARTIFACTS_DIR/lib" + # make libwebrtc.a # don't include nasm -ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` -objcopy --redefine-syms="$COMMAND_DIR/boringssl_prefix_symbols.txt" "$ARTIFACTS_DIR/lib/libwebrtc.a" +"$AR" -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` +"$OBJCOPY" --redefine-syms="$COMMAND_DIR/boringssl_prefix_symbols.txt" "$ARTIFACTS_DIR/lib/libwebrtc.a" python3 "./src/tools_webrtc/libs/generate_licenses.py" \ --target :default "$OUTPUT_DIR" "$OUTPUT_DIR" @@ -141,5 +186,11 @@ cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR" cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" cd src +if [ $chromium_libcxx == "true" ]; then + mkdir -p "$ARTIFACTS_DIR/include/buildtools/third_party" + cp -R buildtools/third_party/libc++ "$ARTIFACTS_DIR/include/buildtools/third_party" + mkdir -p "$ARTIFACTS_DIR/include/third_party/libc++/src" + cp -R third_party/libc++/src/include "$ARTIFACTS_DIR/include/third_party/libc++/src" +fi find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include" find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include" diff --git a/webrtc-sys/src/cxx.cc b/webrtc-sys/src/cxx.cc new file mode 100644 index 000000000..951c91078 --- /dev/null +++ b/webrtc-sys/src/cxx.cc @@ -0,0 +1,870 @@ +// This file was copied from src/cxx.cc in cxx's repository, which is licensed MIT OR Apache-2.0. +// Only the include path for cxx.h has been modified. +#include "rust/cxx.h" +#include +#include +#include +#include + +#ifdef __cpp_lib_bit_cast +#include +#endif + +// Most compilers set __cpp_attributes on C++11 and up, and set __cpp_exceptions +// if the flag `-fno-exceptions` is not set. On these compilers we detect +// `-fno-exceptions` this way. +// +// Some compilers never set either one. On these, rely on the user to do +// `-DRUST_CXX_NO_EXCEPTIONS` if they are not using exceptions. +// +// On MSVC, it is possible for exception throwing and catching to be enabled +// without __cpp_exceptions being defined, so do not try to detect anything. +#if !defined(RUST_CXX_NO_EXCEPTIONS) && defined(__cpp_attributes) && \ + !defined(__cpp_exceptions) && (!defined(_MSC_VER) || defined(__llvm__)) +#define RUST_CXX_NO_EXCEPTIONS +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#endif + +extern "C" { +void cxxbridge1$cxx_string$init(std::string *s, const std::uint8_t *ptr, + std::size_t len) noexcept { + new (s) std::string(reinterpret_cast(ptr), len); +} + +void cxxbridge1$cxx_string$destroy(std::string *s) noexcept { + using std::string; + s->~string(); +} + +const char *cxxbridge1$cxx_string$data(const std::string &s) noexcept { + return s.data(); +} + +std::size_t cxxbridge1$cxx_string$length(const std::string &s) noexcept { + return s.length(); +} + +void cxxbridge1$cxx_string$clear(std::string &s) noexcept { s.clear(); } + +void cxxbridge1$cxx_string$reserve_total(std::string &s, + size_t new_cap) noexcept { + s.reserve(new_cap); +} + +void cxxbridge1$cxx_string$push(std::string &s, const std::uint8_t *ptr, + std::size_t len) noexcept { + s.append(reinterpret_cast(ptr), len); +} + +// rust::String +void cxxbridge1$string$new(rust::String *self) noexcept; +void cxxbridge1$string$clone(rust::String *self, + const rust::String &other) noexcept; +bool cxxbridge1$string$from_utf8(rust::String *self, const char *ptr, + std::size_t len) noexcept; +void cxxbridge1$string$from_utf8_lossy(rust::String *self, const char *ptr, + std::size_t len) noexcept; +bool cxxbridge1$string$from_utf16(rust::String *self, const char16_t *ptr, + std::size_t len) noexcept; +void cxxbridge1$string$from_utf16_lossy(rust::String *self, const char16_t *ptr, + std::size_t len) noexcept; +void cxxbridge1$string$drop(rust::String *self) noexcept; +const char *cxxbridge1$string$ptr(const rust::String *self) noexcept; +std::size_t cxxbridge1$string$len(const rust::String *self) noexcept; +std::size_t cxxbridge1$string$capacity(const rust::String *self) noexcept; +void cxxbridge1$string$reserve_additional(rust::String *self, + size_t additional) noexcept; +void cxxbridge1$string$reserve_total(rust::String *self, + size_t new_cap) noexcept; + +// rust::Str +void cxxbridge1$str$new(rust::Str *self) noexcept; +void cxxbridge1$str$ref(rust::Str *self, const rust::String *string) noexcept; +bool cxxbridge1$str$from(rust::Str *self, const char *ptr, + std::size_t len) noexcept; +const char *cxxbridge1$str$ptr(const rust::Str *self) noexcept; +std::size_t cxxbridge1$str$len(const rust::Str *self) noexcept; + +// rust::Slice +void cxxbridge1$slice$new(void *self, const void *ptr, + std::size_t len) noexcept; +void *cxxbridge1$slice$ptr(const void *self) noexcept; +std::size_t cxxbridge1$slice$len(const void *self) noexcept; +} // extern "C" + +namespace rust { +inline namespace cxxbridge1 { + +template +void panic [[noreturn]] (const char *msg) { +#if defined(RUST_CXX_NO_EXCEPTIONS) + std::fprintf(stderr, "Error: %s. Aborting.\n", msg); + std::abort(); +#else + throw Exception(msg); +#endif +} + +template void panic [[noreturn]] (const char *msg); + +template +static bool is_aligned(const void *ptr) noexcept { + auto iptr = reinterpret_cast(ptr); + return !(iptr % alignof(T)); +} + +String::String() noexcept { cxxbridge1$string$new(this); } + +String::String(const String &other) noexcept { + cxxbridge1$string$clone(this, other); +} + +String::String(String &&other) noexcept : repr(other.repr) { + cxxbridge1$string$new(&other); +} + +String::~String() noexcept { cxxbridge1$string$drop(this); } + +static void initString(String *self, const char *s, std::size_t len) { + if (!cxxbridge1$string$from_utf8(self, s, len)) { + panic("data for rust::String is not utf-8"); + } +} + +static void initString(String *self, const char16_t *s, std::size_t len) { + if (!cxxbridge1$string$from_utf16(self, s, len)) { + panic("data for rust::String is not utf-16"); + } +} + +String::String(const std::string &s) { initString(this, s.data(), s.length()); } + +String::String(const char *s) { + assert(s != nullptr); + initString(this, s, std::strlen(s)); +} + +String::String(const char *s, std::size_t len) { + assert(s != nullptr || len == 0); + initString(this, + s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len); +} + +#ifdef __cpp_char8_t +String::String(const char8_t *s) : String(reinterpret_cast(s)) {} + +String::String(const char8_t *s, std::size_t len) + : String(reinterpret_cast(s), len) {} +#endif + +String::String(const char16_t *s) { + assert(s != nullptr); + assert(is_aligned(s)); + initString(this, s, std::char_traits::length(s)); +} + +String::String(const char16_t *s, std::size_t len) { + assert(s != nullptr || len == 0); + assert(is_aligned(s)); + initString(this, + s == nullptr && len == 0 ? reinterpret_cast(2) + : s, + len); +} + +struct String::lossy_t {}; + +String::String(lossy_t, const char *s, std::size_t len) noexcept { + cxxbridge1$string$from_utf8_lossy( + this, s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len); +} + +String::String(lossy_t, const char16_t *s, std::size_t len) noexcept { + cxxbridge1$string$from_utf16_lossy( + this, + s == nullptr && len == 0 ? reinterpret_cast(2) : s, + len); +} + +String String::lossy(const std::string &s) noexcept { + return String::lossy(s.data(), s.length()); +} + +String String::lossy(const char *s) noexcept { + assert(s != nullptr); + return String::lossy(s, std::strlen(s)); +} + +String String::lossy(const char *s, std::size_t len) noexcept { + assert(s != nullptr || len == 0); + return String(lossy_t{}, s, len); +} + +String String::lossy(const char16_t *s) noexcept { + assert(s != nullptr); + assert(is_aligned(s)); + return String(lossy_t{}, s, std::char_traits::length(s)); +} + +String String::lossy(const char16_t *s, std::size_t len) noexcept { + assert(s != nullptr || len == 0); + assert(is_aligned(s)); + return String(lossy_t{}, s, len); +} + +String &String::operator=(const String &other) & noexcept { + if (this != &other) { + cxxbridge1$string$drop(this); + cxxbridge1$string$clone(this, other); + } + return *this; +} + +String &String::operator=(String &&other) & noexcept { + cxxbridge1$string$drop(this); + this->repr = other.repr; + cxxbridge1$string$new(&other); + return *this; +} + +String::operator std::string() const { + return std::string(this->data(), this->size()); +} + +const char *String::data() const noexcept { + return cxxbridge1$string$ptr(this); +} + +std::size_t String::size() const noexcept { + return cxxbridge1$string$len(this); +} + +std::size_t String::length() const noexcept { + return cxxbridge1$string$len(this); +} + +bool String::empty() const noexcept { return this->size() == 0; } + +const char *String::c_str() noexcept { + auto len = this->length(); + cxxbridge1$string$reserve_additional(this, 1); + auto ptr = this->data(); + const_cast(ptr)[len] = '\0'; + return ptr; +} + +std::size_t String::capacity() const noexcept { + return cxxbridge1$string$capacity(this); +} + +void String::reserve(std::size_t new_cap) noexcept { + cxxbridge1$string$reserve_total(this, new_cap); +} + +String::iterator String::begin() noexcept { + return const_cast(this->data()); +} + +String::iterator String::end() noexcept { + return const_cast(this->data()) + this->size(); +} + +String::const_iterator String::begin() const noexcept { return this->cbegin(); } + +String::const_iterator String::end() const noexcept { return this->cend(); } + +String::const_iterator String::cbegin() const noexcept { return this->data(); } + +String::const_iterator String::cend() const noexcept { + return this->data() + this->size(); +} + +bool String::operator==(const String &rhs) const noexcept { + return rust::Str(*this) == rust::Str(rhs); +} + +bool String::operator!=(const String &rhs) const noexcept { + return rust::Str(*this) != rust::Str(rhs); +} + +bool String::operator<(const String &rhs) const noexcept { + return rust::Str(*this) < rust::Str(rhs); +} + +bool String::operator<=(const String &rhs) const noexcept { + return rust::Str(*this) <= rust::Str(rhs); +} + +bool String::operator>(const String &rhs) const noexcept { + return rust::Str(*this) > rust::Str(rhs); +} + +bool String::operator>=(const String &rhs) const noexcept { + return rust::Str(*this) >= rust::Str(rhs); +} + +void String::swap(String &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +String::String(unsafe_bitcopy_t, const String &bits) noexcept + : repr(bits.repr) {} + +std::ostream &operator<<(std::ostream &os, const String &s) { + os.write(s.data(), static_cast(s.size())); + return os; +} + +Str::Str() noexcept { cxxbridge1$str$new(this); } + +Str::Str(const String &s) noexcept { cxxbridge1$str$ref(this, &s); } + +static void initStr(Str *self, const char *ptr, std::size_t len) { + if (!cxxbridge1$str$from(self, ptr, len)) { + panic("data for rust::Str is not utf-8"); + } +} + +Str::Str(const std::string &s) { initStr(this, s.data(), s.length()); } + +Str::Str(const char *s) { + assert(s != nullptr); + initStr(this, s, std::strlen(s)); +} + +Str::Str(const char *s, std::size_t len) { + assert(s != nullptr || len == 0); + initStr(this, + s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len); +} + +Str::operator std::string() const { + return std::string(this->data(), this->size()); +} + +#if __cplusplus >= 201703L +Str::operator std::string_view() const { + return std::string_view(this->data(), this->size()); +} +#endif + +const char *Str::data() const noexcept { return cxxbridge1$str$ptr(this); } + +std::size_t Str::size() const noexcept { return cxxbridge1$str$len(this); } + +std::size_t Str::length() const noexcept { return this->size(); } + +bool Str::empty() const noexcept { return this->size() == 0; } + +Str::const_iterator Str::begin() const noexcept { return this->cbegin(); } + +Str::const_iterator Str::end() const noexcept { return this->cend(); } + +Str::const_iterator Str::cbegin() const noexcept { return this->data(); } + +Str::const_iterator Str::cend() const noexcept { + return this->data() + this->size(); +} + +bool Str::operator==(const Str &rhs) const noexcept { + return this->size() == rhs.size() && + std::equal(this->begin(), this->end(), rhs.begin()); +} + +bool Str::operator!=(const Str &rhs) const noexcept { return !(*this == rhs); } + +bool Str::operator<(const Str &rhs) const noexcept { + return std::lexicographical_compare(this->begin(), this->end(), rhs.begin(), + rhs.end()); +} + +bool Str::operator<=(const Str &rhs) const noexcept { + // std::mismatch(this->begin(), this->end(), rhs.begin(), rhs.end()), except + // without Undefined Behavior on C++11 if rhs is shorter than *this. + const_iterator liter = this->begin(), lend = this->end(), riter = rhs.begin(), + rend = rhs.end(); + while (liter != lend && riter != rend && *liter == *riter) { + ++liter; + ++riter; + } + if (liter == lend) { + return true; // equal or *this is a prefix of rhs + } else if (riter == rend) { + return false; // rhs is a prefix of *this + } else { + return *liter <= *riter; + } +} + +bool Str::operator>(const Str &rhs) const noexcept { return rhs < *this; } + +bool Str::operator>=(const Str &rhs) const noexcept { return rhs <= *this; } + +void Str::swap(Str &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +std::ostream &operator<<(std::ostream &os, const Str &s) { + os.write(s.data(), static_cast(s.size())); + return os; +} + +void sliceInit(void *self, const void *ptr, std::size_t len) noexcept { + cxxbridge1$slice$new(self, ptr, len); +} + +void *slicePtr(const void *self) noexcept { return cxxbridge1$slice$ptr(self); } + +std::size_t sliceLen(const void *self) noexcept { + return cxxbridge1$slice$len(self); +} + +// Rust specifies that usize is ABI compatible with C's uintptr_t. +// https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize +// However there is no direct Rust equivalent for size_t. C does not guarantee +// that size_t and uintptr_t are compatible. In practice though, on all +// platforms supported by Rust, they are identical for ABI purposes. See the +// libc crate which unconditionally defines libc::size_t = usize. We expect the +// same here and these assertions are just here to explicitly document that. +// *Note that no assumption is made about C++ name mangling of signatures +// containing these types, not here nor anywhere in CXX.* +static_assert(sizeof(std::size_t) == sizeof(std::uintptr_t), + "unsupported size_t size"); +static_assert(alignof(std::size_t) == alignof(std::uintptr_t), + "unsupported size_t alignment"); +static_assert(sizeof(rust::isize) == sizeof(std::intptr_t), + "unsupported ssize_t size"); +static_assert(alignof(rust::isize) == alignof(std::intptr_t), + "unsupported ssize_t alignment"); + +// The C++ standard does not guarantee a particular size, alignment, or bit +// pattern for bool. In practice on all platforms supported by Rust, it is +// compatible with Rust's bool. The libc crate freely uses Rust bool in +// foreign function signatures. +static_assert(sizeof(bool) == 1, "unsupported bool size"); +static_assert(alignof(bool) == 1, "unsupported bool alignment"); +#ifdef __cpp_lib_bit_cast +static_assert(std::bit_cast(false) == 0, + "unsupported bit representation of false"); +static_assert(std::bit_cast(true) == 1, + "unsupported bit representation of true"); +#endif + +static_assert(std::is_trivially_copy_constructible::value, + "trivial Str(const Str &)"); +static_assert(std::is_trivially_copy_assignable::value, + "trivial operator=(const Str &)"); +static_assert(std::is_trivially_destructible::value, "trivial ~Str()"); + +static_assert( + std::is_trivially_copy_constructible>::value, + "trivial Slice(const Slice &)"); +static_assert( + std::is_trivially_move_constructible>::value, + "trivial Slice(Slice &&)"); +static_assert( + std::is_trivially_copy_assignable>::value, + "trivial Slice::operator=(const Slice &) for const slices"); +static_assert( + std::is_trivially_move_assignable>::value, + "trivial Slice::operator=(Slice &&)"); +static_assert(std::is_trivially_destructible>::value, + "trivial ~Slice()"); + +static_assert(std::is_trivially_copy_constructible>::value, + "trivial Slice(const Slice &)"); +static_assert(std::is_trivially_move_constructible>::value, + "trivial Slice(Slice &&)"); +static_assert(!std::is_copy_assignable>::value, + "delete Slice::operator=(const Slice &) for mut slices"); +static_assert(std::is_trivially_move_assignable>::value, + "trivial Slice::operator=(Slice &&)"); +static_assert(std::is_trivially_destructible>::value, + "trivial ~Slice()"); + +static_assert(std::is_same::const_iterator, + Vec::iterator>::value, + "Vec::const_iterator == Vec::iterator"); +static_assert(std::is_same::const_iterator, + Vec::iterator>::value, + "Vec::const_iterator == Vec::iterator"); +static_assert(!std::is_same::const_iterator, + Vec::iterator>::value, + "Vec::const_iterator != Vec::iterator"); + +static const char *errorCopy(const char *ptr, std::size_t len) { + char *copy = new char[len + 1]; + std::memcpy(copy, ptr, len); + copy[len] = '\0'; + return copy; +} + +extern "C" { +const char *cxxbridge1$error(const char *ptr, std::size_t len) noexcept { + return errorCopy(ptr, len); +} +} // extern "C" + +Error::Error(const Error &other) + : std::exception(other), + msg(other.msg ? errorCopy(other.msg, other.len) : nullptr), + len(other.len) {} + +Error::Error(Error &&other) noexcept + : std::exception(std::move(other)), msg(other.msg), len(other.len) { + other.msg = nullptr; + other.len = 0; +} + +Error::~Error() noexcept { delete[] this->msg; } + +Error &Error::operator=(const Error &other) & { + if (this != &other) { + std::exception::operator=(other); + delete[] this->msg; + this->msg = nullptr; + if (other.msg) { + this->msg = errorCopy(other.msg, other.len); + this->len = other.len; + } + } + return *this; +} + +Error &Error::operator=(Error &&other) & noexcept { + std::exception::operator=(std::move(other)); + delete[] this->msg; + this->msg = other.msg; + this->len = other.len; + other.msg = nullptr; + other.len = 0; + return *this; +} + +const char *Error::what() const noexcept { return this->msg; } + +namespace { +template +union MaybeUninit { + T value; + MaybeUninit() {} + ~MaybeUninit() {} +}; +} // namespace + +namespace repr { +struct PtrLen final { + void *ptr; + std::size_t len; +}; +} // namespace repr + +extern "C" { +repr::PtrLen cxxbridge1$exception(const char *, std::size_t len) noexcept; +} + +namespace detail { +// On some platforms size_t is the same C++ type as one of the sized integer +// types; on others it is a distinct type. Only in the latter case do we need to +// define a specialized impl of rust::Vec, because in the former case it +// would collide with one of the other specializations. +using usize_if_unique = + typename std::conditional::value || + std::is_same::value, + struct usize_ignore, size_t>::type; +using isize_if_unique = + typename std::conditional::value || + std::is_same::value, + struct isize_ignore, rust::isize>::type; +// Similarly, on some platforms char may just be an alias for [u]int8_t. +using char_if_unique = + typename std::conditional::value || + std::is_same::value, + struct char_ignore, char>::type; + +class Fail final { + repr::PtrLen &throw$; + +public: + Fail(repr::PtrLen &throw$) noexcept : throw$(throw$) {} + void operator()(const char *) noexcept; + void operator()(const std::string &) noexcept; +}; + +void Fail::operator()(const char *catch$) noexcept { + throw$ = cxxbridge1$exception(catch$, std::strlen(catch$)); +} + +void Fail::operator()(const std::string &catch$) noexcept { + throw$ = cxxbridge1$exception(catch$.data(), catch$.length()); +} +} // namespace detail + +} // namespace cxxbridge1 +} // namespace rust + +namespace { +template +void destroy(T *ptr) { + ptr->~T(); +} +} // namespace + +extern "C" { +void cxxbridge1$unique_ptr$std$string$null( + std::unique_ptr *ptr) noexcept { + new (ptr) std::unique_ptr(); +} +void cxxbridge1$unique_ptr$std$string$raw(std::unique_ptr *ptr, + std::string *raw) noexcept { + new (ptr) std::unique_ptr(raw); +} +const std::string *cxxbridge1$unique_ptr$std$string$get( + const std::unique_ptr &ptr) noexcept { + return ptr.get(); +} +std::string *cxxbridge1$unique_ptr$std$string$release( + std::unique_ptr &ptr) noexcept { + return ptr.release(); +} +void cxxbridge1$unique_ptr$std$string$drop( + std::unique_ptr *ptr) noexcept { + ptr->~unique_ptr(); +} +} // extern "C" + +namespace { +const std::size_t kMaxExpectedWordsInString = 8; +static_assert(alignof(std::string) <= alignof(void *), + "unexpectedly large std::string alignment"); +static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), + "unexpectedly large std::string size"); +} // namespace + +#define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ + std::vector *cxxbridge1$std$vector$##RUST_TYPE##$new() noexcept { \ + return new std::vector(); \ + } \ + std::size_t cxxbridge1$std$vector$##RUST_TYPE##$size( \ + const std::vector &s) noexcept { \ + return s.size(); \ + } \ + std::size_t cxxbridge1$std$vector$##RUST_TYPE##$capacity( \ + const std::vector &s) noexcept { \ + return s.capacity(); \ + } \ + CXX_TYPE *cxxbridge1$std$vector$##RUST_TYPE##$get_unchecked( \ + std::vector *s, std::size_t pos) noexcept { \ + return &(*s)[pos]; \ + } \ + void cxxbridge1$std$vector$##RUST_TYPE##$reserve( \ + std::vector *s, std::size_t new_cap) noexcept { \ + s->reserve(new_cap); \ + } \ + void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$null( \ + std::unique_ptr> *ptr) noexcept { \ + new (ptr) std::unique_ptr>(); \ + } \ + void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$raw( \ + std::unique_ptr> *ptr, \ + std::vector *raw) noexcept { \ + new (ptr) std::unique_ptr>(raw); \ + } \ + const std::vector \ + *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$get( \ + const std::unique_ptr> &ptr) noexcept { \ + return ptr.get(); \ + } \ + std::vector \ + *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$release( \ + std::unique_ptr> &ptr) noexcept { \ + return ptr.release(); \ + } \ + void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$drop( \ + std::unique_ptr> *ptr) noexcept { \ + ptr->~unique_ptr(); \ + } + +#define STD_VECTOR_TRIVIAL_OPS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$std$vector$##RUST_TYPE##$push_back( \ + std::vector *v, CXX_TYPE *value) noexcept { \ + v->push_back(std::move(*value)); \ + destroy(value); \ + } \ + void cxxbridge1$std$vector$##RUST_TYPE##$pop_back(std::vector *v, \ + CXX_TYPE *out) noexcept { \ + new (out) CXX_TYPE(std::move(v->back())); \ + v->pop_back(); \ + } + +#define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$rust_vec$##RUST_TYPE##$new( \ + rust::Vec *ptr) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$drop( \ + rust::Vec *ptr) noexcept; \ + std::size_t cxxbridge1$rust_vec$##RUST_TYPE##$len( \ + const rust::Vec *ptr) noexcept; \ + std::size_t cxxbridge1$rust_vec$##RUST_TYPE##$capacity( \ + const rust::Vec *ptr) noexcept; \ + const CXX_TYPE *cxxbridge1$rust_vec$##RUST_TYPE##$data( \ + const rust::Vec *ptr) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total( \ + rust::Vec *ptr, std::size_t new_cap) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec *ptr, \ + std::size_t len) noexcept; \ + void cxxbridge1$rust_vec$##RUST_TYPE##$truncate(rust::Vec *ptr, \ + std::size_t len) noexcept; + +#define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ + template <> \ + Vec::Vec() noexcept { \ + cxxbridge1$rust_vec$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Vec::drop() noexcept { \ + return cxxbridge1$rust_vec$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + std::size_t Vec::size() const noexcept { \ + return cxxbridge1$rust_vec$##RUST_TYPE##$len(this); \ + } \ + template <> \ + std::size_t Vec::capacity() const noexcept { \ + return cxxbridge1$rust_vec$##RUST_TYPE##$capacity(this); \ + } \ + template <> \ + const CXX_TYPE *Vec::data() const noexcept { \ + return cxxbridge1$rust_vec$##RUST_TYPE##$data(this); \ + } \ + template <> \ + void Vec::reserve_total(std::size_t new_cap) noexcept { \ + cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(this, new_cap); \ + } \ + template <> \ + void Vec::set_len(std::size_t len) noexcept { \ + cxxbridge1$rust_vec$##RUST_TYPE##$set_len(this, len); \ + } \ + template <> \ + void Vec::truncate(std::size_t len) { \ + cxxbridge1$rust_vec$##RUST_TYPE##$truncate(this, len); \ + } + +#define SHARED_PTR_OPS(RUST_TYPE, CXX_TYPE) \ + static_assert(sizeof(std::shared_ptr) == 2 * sizeof(void *), ""); \ + static_assert(alignof(std::shared_ptr) == alignof(void *), ""); \ + void cxxbridge1$std$shared_ptr$##RUST_TYPE##$null( \ + std::shared_ptr *ptr) noexcept { \ + new (ptr) std::shared_ptr(); \ + } \ + void cxxbridge1$std$shared_ptr$##RUST_TYPE##$raw( \ + std::shared_ptr *ptr, CXX_TYPE *raw) noexcept { \ + new (ptr) std::shared_ptr(raw); \ + } \ + CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$uninit( \ + std::shared_ptr *ptr) noexcept { \ + CXX_TYPE *uninit = \ + reinterpret_cast(new rust::MaybeUninit); \ + new (ptr) std::shared_ptr(uninit); \ + return uninit; \ + } \ + void cxxbridge1$std$shared_ptr$##RUST_TYPE##$clone( \ + const std::shared_ptr &self, \ + std::shared_ptr *ptr) noexcept { \ + new (ptr) std::shared_ptr(self); \ + } \ + const CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$get( \ + const std::shared_ptr &self) noexcept { \ + return self.get(); \ + } \ + void cxxbridge1$std$shared_ptr$##RUST_TYPE##$drop( \ + const std::shared_ptr *self) noexcept { \ + self->~shared_ptr(); \ + } \ + static_assert(sizeof(std::weak_ptr) == 2 * sizeof(void *), ""); \ + static_assert(alignof(std::weak_ptr) == alignof(void *), ""); \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$null( \ + std::weak_ptr *ptr) noexcept { \ + new (ptr) std::weak_ptr(); \ + } \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$clone( \ + const std::weak_ptr &self, \ + std::weak_ptr *ptr) noexcept { \ + new (ptr) std::weak_ptr(self); \ + } \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$downgrade( \ + const std::shared_ptr &shared, \ + std::weak_ptr *weak) noexcept { \ + new (weak) std::weak_ptr(shared); \ + } \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$upgrade( \ + const std::weak_ptr &weak, \ + std::shared_ptr *shared) noexcept { \ + new (shared) std::shared_ptr(weak.lock()); \ + } \ + void cxxbridge1$std$weak_ptr$##RUST_TYPE##$drop( \ + const std::weak_ptr *self) noexcept { \ + self->~weak_ptr(); \ + } + +// Usize and isize are the same type as one of the below. +#define FOR_EACH_NUMERIC(MACRO) \ + MACRO(u8, std::uint8_t) \ + MACRO(u16, std::uint16_t) \ + MACRO(u32, std::uint32_t) \ + MACRO(u64, std::uint64_t) \ + MACRO(i8, std::int8_t) \ + MACRO(i16, std::int16_t) \ + MACRO(i32, std::int32_t) \ + MACRO(i64, std::int64_t) \ + MACRO(f32, float) \ + MACRO(f64, double) + +#define FOR_EACH_TRIVIAL_STD_VECTOR(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ + MACRO(usize, std::size_t) \ + MACRO(isize, rust::isize) + +#define FOR_EACH_STD_VECTOR(MACRO) \ + FOR_EACH_TRIVIAL_STD_VECTOR(MACRO) \ + MACRO(string, std::string) + +#define FOR_EACH_RUST_VEC(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ + MACRO(bool, bool) \ + MACRO(char, rust::detail::char_if_unique) \ + MACRO(usize, rust::detail::usize_if_unique) \ + MACRO(isize, rust::detail::isize_if_unique) \ + MACRO(string, rust::String) \ + MACRO(str, rust::Str) + +#define FOR_EACH_SHARED_PTR(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ + MACRO(bool, bool) \ + MACRO(usize, std::size_t) \ + MACRO(isize, rust::isize) \ + MACRO(string, std::string) + +extern "C" { +FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) +FOR_EACH_TRIVIAL_STD_VECTOR(STD_VECTOR_TRIVIAL_OPS) +FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) +FOR_EACH_SHARED_PTR(SHARED_PTR_OPS) +} // extern "C" + +namespace rust { +inline namespace cxxbridge1 { +FOR_EACH_RUST_VEC(RUST_VEC_OPS) +} // namespace cxxbridge1 +} // namespace rust From 6e83d0ef1445b2d9ef956b38a3d16c8040fae544 Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 9 Oct 2025 01:00:33 -0500 Subject: [PATCH 3/3] webrtc-sys: build libwebrtc from source on Linux --- webrtc-sys/build.rs | 50 +++++- webrtc-sys/libwebrtc/build_linux.sh | 81 ++++------ .../libwebrtc/generate-source-archive.sh | 144 ++++++++++++++++++ 3 files changed, 223 insertions(+), 52 deletions(-) create mode 100755 webrtc-sys/libwebrtc/generate-source-archive.sh diff --git a/webrtc-sys/build.rs b/webrtc-sys/build.rs index a907168d3..c699934ad 100644 --- a/webrtc-sys/build.rs +++ b/webrtc-sys/build.rs @@ -79,7 +79,54 @@ fn main() { "src/apm.cpp", ]); - let webrtc_dir = webrtc_sys_build::webrtc_dir(); + let mut webrtc_dir = webrtc_sys_build::webrtc_dir(); + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + + #[cfg(target_os = "linux")] + println!("cargo:rerun-if-env-changed=LK_LIBWEBRTC_SOURCE"); + #[cfg(target_os = "linux")] + if let Ok(source_archive) = env::var("LK_LIBWEBRTC_SOURCE") { + let out_dir = env::var("OUT_DIR").unwrap(); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + fn check_command_output(output: std::process::Output) { + let status = output.status; + if !status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + panic!("Command exited unsuccessfully: {status:?},\nstdout:\n{stdout}\nstderr:\n{stderr}"); + } + } + + check_command_output( + Command::new("tar").args(["-Jxf", &source_archive, "-C", &out_dir]).output().unwrap(), + ); + + let script_arch = match target_arch.as_str() { + "x86_64" => "x64", + "aarch64" => "arm64", + a => a, + }; + check_command_output( + Command::new("bash") + .args([ + &format!("{manifest_dir}/libwebrtc/build_linux.sh"), + "--sources", + &format!("{out_dir}/src"), + "--toolchain", + "gnu", + "--arch", + script_arch, + "--profile", + "release", + ]) + .output() + .unwrap(), + ); + + webrtc_dir = PathBuf::from(format!("{out_dir}/linux-{script_arch}-release")); + } + let webrtc_include = webrtc_dir.join("include"); let webrtc_lib = webrtc_dir.join("lib"); @@ -110,7 +157,6 @@ fn main() { println!("cargo:rustc-link-lib=static=webrtc"); let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); match target_os.as_str() { "windows" => { println!("cargo:rustc-link-lib=dylib=msdmo"); diff --git a/webrtc-sys/libwebrtc/build_linux.sh b/webrtc-sys/libwebrtc/build_linux.sh index 82a5a7eb9..e2ca17f41 100755 --- a/webrtc-sys/libwebrtc/build_linux.sh +++ b/webrtc-sys/libwebrtc/build_linux.sh @@ -20,12 +20,12 @@ toolchain="gnu" while [ "$#" -gt 0 ]; do case "$1" in + --sources) + sources="$(realpath $2)" + shift 2 + ;; --arch) arch="$2" - if [ "$arch" != "x64" ] && [ "$arch" != "arm64" ]; then - echo "Error: Invalid value for --arch. Must be 'x64' or 'arm64'." - exit 1 - fi shift 2 ;; --profile) @@ -56,14 +56,20 @@ if [ -z "$arch" ]; then exit 1 fi +if [ -z "$sources" ]; then + echo "Error: --sources must be set." + exit 1 +fi + echo "Building LiveKit WebRTC - Linux" echo "Arch: $arch" echo "Profile: $profile" echo "Toolchain: $toolchain" -export COMMAND_DIR=$(cd $(dirname $0); pwd) -export OUTPUT_DIR="$(pwd)/build-$arch-$profile" -export ARTIFACTS_DIR="$(pwd)/linux-$arch-$profile" +COMMAND_DIR=$(cd $(dirname $0); pwd) +OUTPUT_DIR="$(dirname "$sources")" +BUILD_DIR="$OUTPUT_DIR/build-$arch-$profile" +ARTIFACTS_DIR="$OUTPUT_DIR/linux-$arch-$profile" if [ "$toolchain" == "gnu" ]; then [ -n "$CC" ] || export CC="$(which gcc)" @@ -93,45 +99,14 @@ elif [ "$toolchain" == "llvm" ]; then custom_toolchain=\"//build/toolchain/linux/unbundle:default\" \ host_toolchain=\"//build/toolchain/linux/unbundle:default\"" elif [ "$toolchain" == "chromium-llvm" ]; then - AR="$COMMAND_DIR/src/third_party/llvm-build/Release+Asserts/bin/llvm-ar" - OBJCOPY="$COMMAND_DIR/src/third_party/llvm-build/Release+Asserts/bin/llvm-objcopy" + AR="$sources/third_party/llvm-build/Release+Asserts/bin/llvm-ar" + OBJCOPY="$sources/third_party/llvm-build/Release+Asserts/bin/llvm-objcopy" chromium_libcxx=true toolchain_gn_args="is_clang=true \ use_custom_libcxx=true \ use_sysroot=true" fi -set -x - -if [ ! -e "$(pwd)/depot_tools" ] -then - git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git -fi - -# must be done after runing `which` to find toolchain's executables above -export PATH="$(pwd)/depot_tools:$PATH" - -if [ ! -e "$(pwd)/src" ]; then - # use --nohooks to avoid the download_from_google_storage hook that takes > 6 minutes - # then manually run the other hooks - gclient sync -D --no-history --nohooks - python3 src/tools/rust/update_rust.py - if [ "$toolchain" == "chromium-llvm" ] || [ "$toolchain" == "llvm" ]; then - python3 src/tools/clang/scripts/update.py - fi - if [ "$toolchain" == "chromium-llvm" ]; then - python3 src/build/linux/sysroot_scripts/install-sysroot.py --arch=x64 - python3 src/build/linux/sysroot_scripts/install-sysroot.py --arch=arm64 - fi -fi - -cd src -git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn -git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn -git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn -git apply "$COMMAND_DIR/patches/david_disable_gun_source_macro.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn -cd .. - debug="false" if [ "$profile" = "debug" ]; then debug="true" @@ -163,29 +138,35 @@ args="is_debug=$debug \ rtc_use_x11=false \ $toolchain_gn_args" -set -e +set -xe + +if [ "$toolchain" == "chromium-llvm" ]; then + python3 "$sources/build/linux/sysroot_scripts/install-sysroot.py" --arch=x64 + python3 "$sources/build/linux/sysroot_scripts/install-sysroot.py" --arch=arm64 +fi # generate ninja files -gn gen "$OUTPUT_DIR" --root="src" --args="${args}" +export PATH="$sources/depot_tools:$PATH" +gn gen "$BUILD_DIR" --root="$sources" --args="${args}" # build static library -ninja -C "$OUTPUT_DIR" :default +ninja -C "$BUILD_DIR" :default mkdir -p "$ARTIFACTS_DIR/lib" # make libwebrtc.a # don't include nasm -"$AR" -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` +"$AR" -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$BUILD_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` "$OBJCOPY" --redefine-syms="$COMMAND_DIR/boringssl_prefix_symbols.txt" "$ARTIFACTS_DIR/lib/libwebrtc.a" -python3 "./src/tools_webrtc/libs/generate_licenses.py" \ - --target :default "$OUTPUT_DIR" "$OUTPUT_DIR" +python3 "$sources/tools_webrtc/libs/generate_licenses.py" \ + --target :default "$BUILD_DIR" "$BUILD_DIR" -cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR" -cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR" -cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" +cp "$BUILD_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR" +cp "$BUILD_DIR/args.gn" "$ARTIFACTS_DIR" +cp "$BUILD_DIR/LICENSE.md" "$ARTIFACTS_DIR" -cd src +cd $sources if [ $chromium_libcxx == "true" ]; then mkdir -p "$ARTIFACTS_DIR/include/buildtools/third_party" cp -R buildtools/third_party/libc++ "$ARTIFACTS_DIR/include/buildtools/third_party" diff --git a/webrtc-sys/libwebrtc/generate-source-archive.sh b/webrtc-sys/libwebrtc/generate-source-archive.sh new file mode 100755 index 000000000..a9c380bd5 --- /dev/null +++ b/webrtc-sys/libwebrtc/generate-source-archive.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +COMMAND_DIR="$(cd $(dirname $0); pwd)" +SOURCE_DIR="$COMMAND_DIR/src" +ARCHIVE_PATH="$COMMAND_DIR/libwebrtc-source.tar.xz" + +set -x + +# for debugging this script +reset() { + rm -rf "$SOURCE_DIR" "$COMMAND_DIR/depot_tools" "$ARCHIVE_PATH" +} + +download() { + cd "$COMMAND_DIR" + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + # use --nohooks to avoid the download_from_google_storage hook that takes > 6 minutes + # then manually run the other hooks + export PATH="$COMMAND_DIR/depot_tools:$PATH" + gclient sync -D --no-history --nohooks + python3 "$SOURCE_DIR/tools/rust/update_rust.py" + python3 "$SOURCE_DIR/tools/clang/scripts/update.py" + mv depot_tools src +} + +patch() { + cd $SOURCE_DIR + git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + cd third_party + git apply "$COMMAND_DIR/patches/david_disable_gun_source_macro.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +} + +clean() { + required_files_and_dirs=( + abseil-cpp + boringssl + catapult + closure_compiler + dav1d + ffmpeg + freetype + googletest + harfbuzz-ng + icu + jsoncpp + libaom + libjpeg_turbo + libjpeg.gni + BUILD.gn + libpng + libsrtp + libvpx + libyuv + nasm + openh264 + opus + perfetto + protobuf + protobuf-javascript + rust + rust-toolchain + zlib + fuzztest + crc32c + pffft + re2 + google_benchmark + rnnoise + ) + for filename in $(ls -1 "$SOURCE_DIR/third_party"); do + if ! [[ ${required_files_and_dirs[@]} =~ $filename ]]; then + rm -rf "$SOURCE_DIR/third_party/$filename" + fi + done + + third_party_dirs=( + rust-toolchain/bin + rust-toolchain/lib + catapult/tracing/test_data + catapult/third_party/vinn/third_party/v8 + catapult/third_party/gsutil + rust/chromium_crates_io + boringssl/src/third_party/wycheproof_testvectors + openh264/src/res + ffmpeg/tests + harfbuzz-ng/src/test + harfbuzz-ng/src/perf + perfetto/src/traced/probes/ftrace/test + perfetto/src/trace_processor + perfetto/docs + perfetto/test + perfetto/ui + icu/source/data + icu/source/test + icu/source + boringssl/src/crypto/cipher/test + boringssl/src/fuzz + opus/src/dnn/torch/osce/resources + nasm/travis + nasm/test + closure_compiler/compiler + ) + for dir in "${third_party_dirs[@]}"; do + rm -rf "$SOURCE_DIR/third_party/$dir" + done + + src_dirs=( + depot_tools/.cipd_bin + depot_tools/.cipd_client + buildtools/reclient + tools/perf/testdata + tools/luci-go + tools/metrics + tools/resultdb + tools/perf/page_sets + tools/perf/core/shard_maps/timing_data + tools/disable_tests/tests + data + base/test + ) + for dir in "${src_dirs[@]}"; do + rm -rf "$SOURCE_DIR/$dir" + done + + dir_names=(testdata test_data doc docs \.git) + for dir_name in "${dir_names[@]}"; do + for dir in $(find "$SOURCE_DIR" -name $dir_name); do + rm -rf "$dir" + done + done +} + +package() { + tar -Jcf "$ARCHIVE_PATH" -C "$COMMAND_DIR" src +} + +reset +set -e +download +patch +clean +package