summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/build/rust
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/build/rust')
-rw-r--r--third_party/libwebrtc/build/rust/OWNERS8
-rw-r--r--third_party/libwebrtc/build/rust/std/BUILD.gn153
-rwxr-xr-xthird_party/libwebrtc/build/rust/std/find_std_rlibs.py97
-rw-r--r--third_party/libwebrtc/build/rust/std/immediate_crash.h170
-rw-r--r--third_party/libwebrtc/build/rust/std/remap_alloc.c70
-rw-r--r--third_party/libwebrtc/build/rust/tests/BUILD.gn22
-rw-r--r--third_party/libwebrtc/build/rust/tests/test_cpp_exe_including_rust/BUILD.gn8
-rw-r--r--third_party/libwebrtc/build/rust/tests/test_cpp_exe_including_rust/main.cc11
-rw-r--r--third_party/libwebrtc/build/rust/tests/test_rust_exe/BUILD.gn12
-rw-r--r--third_party/libwebrtc/build/rust/tests/test_rust_exe/main.rs9
-rw-r--r--third_party/libwebrtc/build/rust/tests/test_rust_source_set/BUILD.gn42
-rw-r--r--third_party/libwebrtc/build/rust/tests/test_rust_source_set/main.rs21
12 files changed, 623 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/rust/OWNERS b/third_party/libwebrtc/build/rust/OWNERS
new file mode 100644
index 0000000000..e15cf8c77f
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/OWNERS
@@ -0,0 +1,8 @@
+adetaylor@chromium.org
+ajgo@chromium.org
+danakj@chromium.org
+jclinton@chromium.org
+lukasza@chromium.org
+palmer@chromium.org
+rsesek@chromium.org
+thakis@chromium.org
diff --git a/third_party/libwebrtc/build/rust/std/BUILD.gn b/third_party/libwebrtc/build/rust/std/BUILD.gn
new file mode 100644
index 0000000000..7532439364
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/std/BUILD.gn
@@ -0,0 +1,153 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This file provides the ability for our C++ toolchain to successfully
+# link binaries containing arbitrary Rust code.
+#
+# By "arbitrary Rust code" I mean .rlib archives full of Rust code, which
+# is actually a static archive.
+#
+# Those static libraries don't link as-is into a final executable because
+# they're designed for downstream processing by further invocations of rustc
+# which link into a final binary. That final invocation of rustc knows how
+# to do two things:
+# * Find the Rust standard library.
+# * Remap some generic allocator symbols to the specific allocator symbols
+# in use.
+# This file does both those things. Any C++ target containing Rust .rlibs
+# should simply depend on :std within this file and it will be taken care of.
+# In practice, this will in future be taken care of by a standard template
+# used for each Rust source set, so that a typical user of Rust need not
+# think about it.
+#
+# This is obviously a bit fragile - rustc might do other magic in future.
+# But, linking with a final C++ toolchain is something often needed, and
+# https://github.com/rust-lang/rust/issues/64191 aims to make this
+# officially possible.
+
+import("//build/config/compiler/compiler.gni")
+import("//build/config/rust.gni")
+
+stdlib_files = [
+ "std", # List first because it makes depfiles more debuggable (see below)
+ "addr2line",
+ "adler",
+ "alloc",
+ "cfg_if",
+ "compiler_builtins",
+ "core",
+ "getopts",
+ "gimli",
+ "hashbrown",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "panic_abort",
+ "panic_unwind",
+ "proc_macro",
+ "rustc_demangle",
+ "std_detect",
+ "term",
+ "test",
+ "unicode_width",
+ "unwind",
+]
+
+if (!use_unverified_rust_toolchain) {
+ # rlib files which are distributed alongside Rust's prebuilt stdlib, but we
+ # don't need to pass to the C++ linker because they're used for specialized
+ # purposes.
+ skip_stdlib_files = [
+ "profiler_builtins",
+ "rustc_std_workspace_alloc",
+ "rustc_std_workspace_core",
+ "rustc_std_workspace_std",
+ ]
+}
+
+if (toolchain_has_rust) {
+ action("find_stdlib") {
+ # Specifics of what we're doing here.
+ #
+ # We are using prebuilt Rust rlibs supplied along with the toolchain.
+ # The Rust standard library consists of rlibs with roughly all the names
+ # above.
+ #
+ # However, their filenames are not predictable, and therefore we can't
+ # have ninja rules which depend upon them. (gn offers a facility to
+ # build rules dynamically, but it's frowned upon because a script needs
+ # to run each time).
+ #
+ # Instead therefore we copy these unpredictable .rlib paths to apredictable
+ # location. That's what this script does. Furthermore, it generates a
+ # .d file in order to teach Ninja that it only needs to do this copying
+ # once, unless the source .rlibs change.
+ #
+ # The script accepts the list of known libraries and will raise an
+ # exception if the list on disk differs. (Either 'Found stdlib rlib
+ # that wasn't expected' or 'We failed to find all expected stdlib
+ # rlibs').
+ script = "find_std_rlibs.py"
+ depfile = "$target_out_dir/stdlib.d"
+ out_libdir = rebase_path(target_out_dir, root_build_dir)
+ out_depfile = rebase_path(depfile, root_build_dir)
+ args = [
+ "--rust-bin-dir",
+ rust_prefix,
+ "--output",
+ out_libdir,
+ "--depfile",
+ out_depfile,
+
+ # Due to limitations in Ninja's handling of .d files, we have to pick
+ # *the first* of our outputs. To make diagnostics more obviously
+ # related to the Rust standard library, we ensure libstd.rlib is first.
+ "--depfile-target",
+ stdlib_files[0],
+ ]
+ if (!use_unverified_rust_toolchain) {
+ args += [
+ "--stdlibs",
+ string_join(",", stdlib_files),
+ "--skip",
+ string_join(",", skip_stdlib_files),
+ ]
+ }
+ if (rust_abi_target != "") {
+ args += [
+ "--target",
+ rust_abi_target,
+ ]
+ }
+
+ outputs = []
+ foreach(lib, stdlib_files) {
+ outputs += [ "$target_out_dir/lib$lib.rlib" ]
+ }
+ }
+
+ config("rust_stdlib_config") {
+ ldflags = []
+ out_libdir = rebase_path(target_out_dir, root_build_dir)
+ foreach(lib, stdlib_files) {
+ this_file = "$out_libdir/lib$lib.rlib"
+ ldflags += [ this_file ]
+ }
+ }
+
+ source_set("remap_alloc") {
+ sources = [
+ "immediate_crash.h",
+ "remap_alloc.c",
+ ]
+ }
+
+ group("std") {
+ all_dependent_configs = [ ":rust_stdlib_config" ]
+ deps = [
+ ":find_stdlib",
+ ":remap_alloc",
+ ]
+ }
+}
diff --git a/third_party/libwebrtc/build/rust/std/find_std_rlibs.py b/third_party/libwebrtc/build/rust/std/find_std_rlibs.py
new file mode 100755
index 0000000000..b8f775e438
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/std/find_std_rlibs.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env/python3
+
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# See BUILD.gn in this directory for an explanation of what this script is for.
+
+import argparse
+import os
+import sys
+import shutil
+import subprocess
+import re
+
+REMOVE_METADATA_SUFFIX_RE = re.compile(b"-[0-9a-f]*", re.I)
+
+
+def expand_name(concise_name):
+ return "lib%s.rlib" % concise_name
+
+
+def main():
+ parser = argparse.ArgumentParser("find_std_rlibs.py")
+ parser.add_argument("--rust-bin-dir",
+ help="Path to Rust binaries",
+ required=True),
+ parser.add_argument("--target", help="Rust target triple", required=False),
+ parser.add_argument("--output",
+ help="Path to rlibs without suffixes",
+ required=True)
+ parser.add_argument("--depfile", help="Path to write depfile", required=True)
+ parser.add_argument("--depfile-target",
+ help="Target to key depfile around",
+ required=True)
+ parser.add_argument("--stdlibs",
+ help="Expected list of standard library libraries")
+ parser.add_argument("--skip-stdlibs",
+ help="Standard library files to skip",
+ default="")
+ args = parser.parse_args()
+ # Ensure we handle each rlib in the expected list exactly once.
+ if args.stdlibs:
+ rlibs_expected = [expand_name(x) for x in args.stdlibs.split(',')]
+ else:
+ rlibs_expected = None
+ rlibs_to_skip = [expand_name(x) for x in args.skip_stdlibs.split(',')]
+ # Ask rustc where to find the stdlib for this target.
+ rustc = os.path.join(args.rust_bin_dir, "rustc")
+ rustc_args = [rustc, "--print", "target-libdir"]
+ if args.target:
+ rustc_args.extend(["--target", args.target])
+ rustlib_dir = subprocess.check_output(rustc_args).rstrip()
+ # Copy the rlibs to a predictable location. Whilst we're doing so,
+ # also write a .d file so that ninja knows it doesn't need to do this
+ # again unless the source rlibs change.
+ # Format:
+ # <output path to>/lib<lib name.rlib>: <path to each Rust stlib rlib>
+ with open(args.depfile, 'w') as depfile:
+ # Ninja isn't versatile at understanding depfiles. We have to say that a
+ # single output depends on all the inputs. We choose any one of the
+ # output rlibs for that purpose. If any of the input rlibs change, ninja
+ # will run this script again and we'll copy them all afresh.
+ depfile.write("%s:" %
+ (os.path.join(args.output, expand_name(args.depfile_target))))
+ for f in os.listdir(rustlib_dir):
+ if f.endswith(b'.rlib'):
+ # As standard Rust includes a hash on the end of each filename
+ # representing certain metadata, to ensure that clients will link
+ # against the correct version. As gn will be manually passing
+ # the correct file path to our linker invocations, we don't need
+ # that, and it would prevent us having the predictable filenames
+ # which we need for statically computable gn dependency rules.
+ (concise_name, count) = REMOVE_METADATA_SUFFIX_RE.subn(b"", f)
+ if count == 0:
+ raise Exception("Unable to remove suffix from %s" % f)
+ if concise_name.decode() in rlibs_to_skip:
+ continue
+ if rlibs_expected is not None:
+ if concise_name.decode() not in rlibs_expected:
+ raise Exception("Found stdlib rlib that wasn't expected: %s" %
+ concise_name)
+ rlibs_expected.remove(concise_name.decode())
+ infile = os.path.join(rustlib_dir, f)
+ outfile = os.path.join(str.encode(args.output), concise_name)
+ depfile.write(" %s" % (infile.decode()))
+ if (not os.path.exists(outfile)
+ or os.stat(infile).st_mtime > os.stat(outfile).st_mtime):
+ shutil.copy(infile, outfile)
+ depfile.write("\n")
+ if rlibs_expected:
+ raise Exception("We failed to find all expected stdlib rlibs: %s" %
+ ','.join(rlibs_expected))
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/third_party/libwebrtc/build/rust/std/immediate_crash.h b/third_party/libwebrtc/build/rust/std/immediate_crash.h
new file mode 100644
index 0000000000..9874bdcd48
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/std/immediate_crash.h
@@ -0,0 +1,170 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is copied from //base/immediate_crash.h.
+
+#ifndef BUILD_RUST_STD_IMMEDIATE_CRASH_H_
+#define BUILD_RUST_STD_IMMEDIATE_CRASH_H_
+
+#include "build/build_config.h"
+
+// Crashes in the fastest possible way with no attempt at logging.
+// There are several constraints; see http://crbug.com/664209 for more context.
+//
+// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the
+// resulting exception or simply hit 'continue' to skip over it in a debugger.
+// - Different instances of TRAP_SEQUENCE_() must not be folded together, to
+// ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile
+// blocks will not be folded together.
+// Note: TRAP_SEQUENCE_() previously required an instruction with a unique
+// nonce since unlike clang, GCC folds together identical asm volatile
+// blocks.
+// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid
+// memory access.
+// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.
+// __builtin_unreachable() is used to provide that hint here. clang also uses
+// this as a heuristic to pack the instructions in the function epilogue to
+// improve code density.
+//
+// Additional properties that are nice to have:
+// - TRAP_SEQUENCE_() should be as compact as possible.
+// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid
+// shifting crash reporting clusters. As a consequence of this, explicit
+// assembly is preferred over intrinsics.
+// Note: this last bullet point may no longer be true, and may be removed in
+// the future.
+
+// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact
+// that clang emits an actual instruction for __builtin_unreachable() on certain
+// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will
+// be removed in followups, so splitting it up like this now makes it easy to
+// land the followups.
+
+#if defined(COMPILER_GCC)
+
+#if defined(OS_NACL)
+
+// Crash report accuracy is not guaranteed on NaCl.
+#define TRAP_SEQUENCE1_() __builtin_trap()
+#define TRAP_SEQUENCE2_() asm volatile("")
+
+#elif defined(ARCH_CPU_X86_FAMILY)
+
+// TODO(https://crbug.com/958675): In theory, it should be possible to use just
+// int3. However, there are a number of crashes with SIGILL as the exception
+// code, so it seems likely that there's a signal handler that allows execution
+// to continue after SIGTRAP.
+#define TRAP_SEQUENCE1_() asm volatile("int3")
+
+#if defined(OS_APPLE)
+// Intentionally empty: __builtin_unreachable() is always part of the sequence
+// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac.
+#define TRAP_SEQUENCE2_() asm volatile("")
+#else
+#define TRAP_SEQUENCE2_() asm volatile("ud2")
+#endif // defined(OS_APPLE)
+
+#elif defined(ARCH_CPU_ARMEL)
+
+// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
+// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
+// cause a SIGTRAP from userspace without using a syscall (which would be a
+// problem for sandboxing).
+// TODO(https://crbug.com/958675): Remove bkpt from this sequence.
+#define TRAP_SEQUENCE1_() asm volatile("bkpt #0")
+#define TRAP_SEQUENCE2_() asm volatile("udf #0")
+
+#elif defined(ARCH_CPU_ARM64)
+
+// This will always generate a SIGTRAP on arm64.
+// TODO(https://crbug.com/958675): Remove brk from this sequence.
+#define TRAP_SEQUENCE1_() asm volatile("brk #0")
+#define TRAP_SEQUENCE2_() asm volatile("hlt #0")
+
+#else
+
+// Crash report accuracy will not be guaranteed on other architectures, but at
+// least this will crash as expected.
+#define TRAP_SEQUENCE1_() __builtin_trap()
+#define TRAP_SEQUENCE2_() asm volatile("")
+
+#endif // ARCH_CPU_*
+
+#elif defined(COMPILER_MSVC)
+
+#if !defined(__clang__)
+
+// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.
+#define TRAP_SEQUENCE1_() __debugbreak()
+#define TRAP_SEQUENCE2_()
+
+#elif defined(ARCH_CPU_ARM64)
+
+// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and
+// __debugbreak() generates that in both VC++ and clang.
+#define TRAP_SEQUENCE1_() __debugbreak()
+// Intentionally empty: __builtin_unreachable() is always part of the sequence
+// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
+// https://crbug.com/958373
+#define TRAP_SEQUENCE2_() __asm volatile("")
+
+#else
+
+#define TRAP_SEQUENCE1_() asm volatile("int3")
+#define TRAP_SEQUENCE2_() asm volatile("ud2")
+
+#endif // __clang__
+
+#else
+
+#error No supported trap sequence!
+
+#endif // COMPILER_GCC
+
+#define TRAP_SEQUENCE_() \
+ do { \
+ TRAP_SEQUENCE1_(); \
+ TRAP_SEQUENCE2_(); \
+ } while (false)
+
+// CHECK() and the trap sequence can be invoked from a constexpr function.
+// This could make compilation fail on GCC, as it forbids directly using inline
+// asm inside a constexpr function. However, it allows calling a lambda
+// expression including the same asm.
+// The side effect is that the top of the stacktrace will not point to the
+// calling function, but to this anonymous lambda. This is still useful as the
+// full name of the lambda will typically include the name of the function that
+// calls CHECK() and the debugger will still break at the right line of code.
+#if !defined(COMPILER_GCC)
+
+#define WRAPPED_TRAP_SEQUENCE_() TRAP_SEQUENCE_()
+
+#else
+
+#define WRAPPED_TRAP_SEQUENCE_() \
+ do { \
+ [] { TRAP_SEQUENCE_(); }(); \
+ } while (false)
+
+#endif // !defined(COMPILER_GCC)
+
+#if defined(__clang__) || defined(COMPILER_GCC)
+
+// __builtin_unreachable() hints to the compiler that this is noreturn and can
+// be packed in the function epilogue.
+#define IMMEDIATE_CRASH() \
+ ({ \
+ WRAPPED_TRAP_SEQUENCE_(); \
+ __builtin_unreachable(); \
+ })
+
+#else
+
+// This is supporting non-chromium user of logging.h to build with MSVC, like
+// pdfium. On MSVC there is no __builtin_unreachable().
+#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE_()
+
+#endif // defined(__clang__) || defined(COMPILER_GCC)
+
+#endif // BUILD_RUST_STD_IMMEDIATE_CRASH_H_
diff --git a/third_party/libwebrtc/build/rust/std/remap_alloc.c b/third_party/libwebrtc/build/rust/std/remap_alloc.c
new file mode 100644
index 0000000000..ba3b2b0671
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/std/remap_alloc.c
@@ -0,0 +1,70 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "build/rust/std/immediate_crash.h"
+
+// When linking a final binary, rustc has to pick between either:
+// * The default Rust allocator
+// * Any #[global_allocator] defined in *any rlib in its dependency tree*
+// (https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html)
+//
+// In this latter case, this fact will be recorded in some of the metadata
+// within the .rlib file. (An .rlib file is just a .a file, but does have
+// additional metadata for use by rustc. This is, as far as I know, the only
+// such metadata we would ideally care about.)
+//
+// In all the linked rlibs,
+// * If 0 crates define a #[global_allocator], rustc uses its default allocator
+// * If 1 crate defines a #[global_allocator], rustc uses that
+// * If >1 crates define a #[global_allocator], rustc bombs out.
+//
+// Because rustc does these checks, it doesn't just have the __rust_alloc
+// symbols defined anywhere (neither in the stdlib nor in any of these
+// crates which have a #[global_allocator] defined.)
+//
+// Instead:
+// Rust's final linking stage invokes dynamic LLVM codegen to create symbols
+// for the basic heap allocation operations. It literally creates a
+// __rust_alloc symbol at link time. Unless any crate has specified a
+// #[global_allocator], it simply calls from __rust_alloc into
+// __rdl_alloc, which is the default Rust allocator. The same applies to a
+// few other symbols.
+//
+// We're not (always) using rustc for final linking. For cases where we're not
+// Rustc as the final linker, we'll define those symbols here instead.
+//
+// In future, we may wish to do something different from using the Rust
+// default allocator (e.g. explicitly redirect to PartitionAlloc). We could
+// do that here, or we could build a crate with a #[global_allocator] and
+// redirect these symbols to that crate instead. The advantage of the latter
+// is that it would work equally well for those cases where rustc is doing
+// the final linking.
+
+void* __rdl_alloc(size_t, size_t);
+void __rdl_dealloc(void*);
+void* __rdl_realloc(void*, size_t, size_t, size_t);
+void* __rdl_alloc_zeroed(size_t, size_t);
+
+void* __rust_alloc(size_t a, size_t b) {
+ return __rdl_alloc(a, b);
+}
+
+void __rust_dealloc(void* a) {
+ __rdl_dealloc(a);
+}
+
+void* __rust_realloc(void* a, size_t b, size_t c, size_t d) {
+ return __rdl_realloc(a, b, c, d);
+}
+
+void* __rust_alloc_zeroed(size_t a, size_t b) {
+ return __rdl_alloc_zeroed(a, b);
+}
+
+void __rust_alloc_error_handler(size_t a, size_t b) {
+ IMMEDIATE_CRASH();
+}
diff --git a/third_party/libwebrtc/build/rust/tests/BUILD.gn b/third_party/libwebrtc/build/rust/tests/BUILD.gn
new file mode 100644
index 0000000000..87a299f21f
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/tests/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/rust.gni")
+
+group("tests") {
+ # Build some minimal binaries to exercise the Rust toolchain
+ # only if that toolchain is enabled in gn args.
+ if (toolchain_has_rust) {
+ deps = [
+ "test_cpp_exe_including_rust",
+ "test_rust_source_set",
+ ]
+ if (build_rust_unit_tests) {
+ deps += [ "test_rust_source_set:test_rust_source_set_unittests" ]
+ }
+ if (rustc_can_link) {
+ deps += [ "test_rust_exe" ]
+ }
+ }
+}
diff --git a/third_party/libwebrtc/build/rust/tests/test_cpp_exe_including_rust/BUILD.gn b/third_party/libwebrtc/build/rust/tests/test_cpp_exe_including_rust/BUILD.gn
new file mode 100644
index 0000000000..a5bdb3dc6c
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/tests/test_cpp_exe_including_rust/BUILD.gn
@@ -0,0 +1,8 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+executable("test_cpp_exe_including_rust") {
+ sources = [ "main.cc" ]
+ deps = [ "//build/rust/tests/test_rust_source_set:test_rust_source_set_lib" ]
+}
diff --git a/third_party/libwebrtc/build/rust/tests/test_cpp_exe_including_rust/main.cc b/third_party/libwebrtc/build/rust/tests/test_cpp_exe_including_rust/main.cc
new file mode 100644
index 0000000000..c1de55af1c
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/tests/test_cpp_exe_including_rust/main.cc
@@ -0,0 +1,11 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defined in Rust.
+extern "C" void say_hello_from_cpp();
+
+int main(int argc, char* argv[]) {
+ say_hello_from_cpp();
+ return 0;
+}
diff --git a/third_party/libwebrtc/build/rust/tests/test_rust_exe/BUILD.gn b/third_party/libwebrtc/build/rust/tests/test_rust_exe/BUILD.gn
new file mode 100644
index 0000000000..0ce2e4a507
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/tests/test_rust_exe/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+executable("test_rust_exe") {
+ crate_root = "main.rs"
+ deps = [ "//build/rust/tests/test_rust_source_set" ]
+ rustflags = [
+ "--edition",
+ "2018",
+ ]
+}
diff --git a/third_party/libwebrtc/build/rust/tests/test_rust_exe/main.rs b/third_party/libwebrtc/build/rust/tests/test_rust_exe/main.rs
new file mode 100644
index 0000000000..c1072054b8
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/tests/test_rust_exe/main.rs
@@ -0,0 +1,9 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use test_rust_source_set::say_hello;
+
+fn main() {
+ say_hello();
+}
diff --git a/third_party/libwebrtc/build/rust/tests/test_rust_source_set/BUILD.gn b/third_party/libwebrtc/build/rust/tests/test_rust_source_set/BUILD.gn
new file mode 100644
index 0000000000..0dc9c540e1
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/tests/test_rust_source_set/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/compiler/compiler.gni")
+import("//build/config/rust.gni")
+
+# A future CL will provide a gn template to generate all the following
+# automatically. We anticipate calling that template "rust_source_set"
+# which is why this test component is named the way it is.
+
+# Dependent Rust targets should depend on this.
+rust_library("test_rust_source_set") {
+ crate_name = "test_rust_source_set"
+ crate_root = "main.rs"
+ rustflags = [
+ "--edition",
+ "2018",
+ ]
+}
+
+# Dependent C++ targets should depend on this,
+# rather than the rlib above
+group("test_rust_source_set_lib") {
+ deps = [
+ ":test_rust_source_set",
+ "//build/rust/std",
+ ]
+}
+
+if (build_rust_unit_tests) {
+ executable("test_rust_source_set_unittests") {
+ crate_root = "main.rs"
+ rustflags = [
+ "--cfg",
+ "feature=\"test\"",
+ "--test",
+ "--edition",
+ "2018",
+ ]
+ }
+}
diff --git a/third_party/libwebrtc/build/rust/tests/test_rust_source_set/main.rs b/third_party/libwebrtc/build/rust/tests/test_rust_source_set/main.rs
new file mode 100644
index 0000000000..f0db1a3065
--- /dev/null
+++ b/third_party/libwebrtc/build/rust/tests/test_rust_source_set/main.rs
@@ -0,0 +1,21 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[no_mangle]
+pub extern "C" fn say_hello_from_cpp() {
+ say_hello();
+}
+
+pub fn say_hello() {
+ println!("Hello, world - from a Rust library. Calculations suggest that 3+4={}", do_maths());
+}
+
+fn do_maths() -> i32 {
+ 3 + 4
+}
+
+#[test]
+fn test_hello() {
+ assert_eq!(7, do_maths());
+}