diff --git a/ports/webrtc/absl-labels.txt b/ports/webrtc/absl-labels.txt new file mode 100644 index 00000000000000..8df0fb323eec54 --- /dev/null +++ b/ports/webrtc/absl-labels.txt @@ -0,0 +1,208 @@ +//third_party/abseil-cpp:absl +//third_party/abseil-cpp:absl_component_deps +//third_party/abseil-cpp/absl/algorithm:algorithm +//third_party/abseil-cpp/absl/algorithm:container +//third_party/abseil-cpp/absl/base:atomic_hook +//third_party/abseil-cpp/absl/base:base +//third_party/abseil-cpp/absl/base:base_internal +//third_party/abseil-cpp/absl/base:config +//third_party/abseil-cpp/absl/base:core_headers +//third_party/abseil-cpp/absl/base:cycleclock_internal +//third_party/abseil-cpp/absl/base:dynamic_annotations +//third_party/abseil-cpp/absl/base:endian +//third_party/abseil-cpp/absl/base:errno_saver +//third_party/abseil-cpp/absl/base:fast_type_id +//third_party/abseil-cpp/absl/base:iterator_traits_internal +//third_party/abseil-cpp/absl/base:log_severity +//third_party/abseil-cpp/absl/base:malloc_internal +//third_party/abseil-cpp/absl/base:no_destructor +//third_party/abseil-cpp/absl/base:nullability +//third_party/abseil-cpp/absl/base:nullability_traits_internal +//third_party/abseil-cpp/absl/base:prefetch +//third_party/abseil-cpp/absl/base:raw_logging_internal +//third_party/abseil-cpp/absl/base:spinlock_wait +//third_party/abseil-cpp/absl/base:strerror +//third_party/abseil-cpp/absl/base:throw_delegate +//third_party/abseil-cpp/absl/base:tracing_internal +//third_party/abseil-cpp/absl/cleanup:cleanup +//third_party/abseil-cpp/absl/cleanup:cleanup_internal +//third_party/abseil-cpp/absl/container:btree +//third_party/abseil-cpp/absl/container:common +//third_party/abseil-cpp/absl/container:common_policy_traits +//third_party/abseil-cpp/absl/container:compressed_tuple +//third_party/abseil-cpp/absl/container:container_memory +//third_party/abseil-cpp/absl/container:fixed_array +//third_party/abseil-cpp/absl/container:flat_hash_map +//third_party/abseil-cpp/absl/container:flat_hash_set +//third_party/abseil-cpp/absl/container:hash_container_defaults +//third_party/abseil-cpp/absl/container:hash_function_defaults +//third_party/abseil-cpp/absl/container:hash_policy_traits +//third_party/abseil-cpp/absl/container:hashtable_control_bytes +//third_party/abseil-cpp/absl/container:hashtable_debug_hooks +//third_party/abseil-cpp/absl/container:hashtablez_sampler +//third_party/abseil-cpp/absl/container:inlined_vector +//third_party/abseil-cpp/absl/container:inlined_vector_internal +//third_party/abseil-cpp/absl/container:layout +//third_party/abseil-cpp/absl/container:linked_hash_map +//third_party/abseil-cpp/absl/container:linked_hash_set +//third_party/abseil-cpp/absl/container:node_hash_map +//third_party/abseil-cpp/absl/container:node_hash_set +//third_party/abseil-cpp/absl/container:node_slot_policy +//third_party/abseil-cpp/absl/container:raw_hash_map +//third_party/abseil-cpp/absl/container:raw_hash_set +//third_party/abseil-cpp/absl/container:raw_hash_set_resize_impl +//third_party/abseil-cpp/absl/crc:cpu_detect +//third_party/abseil-cpp/absl/crc:crc32c +//third_party/abseil-cpp/absl/crc:crc_cord_state +//third_party/abseil-cpp/absl/crc:crc_internal +//third_party/abseil-cpp/absl/crc:non_temporal_arm_intrinsics +//third_party/abseil-cpp/absl/crc:non_temporal_memcpy +//third_party/abseil-cpp/absl/debugging:bounded_utf8_length_sequence +//third_party/abseil-cpp/absl/debugging:debugging_internal +//third_party/abseil-cpp/absl/debugging:decode_rust_punycode +//third_party/abseil-cpp/absl/debugging:demangle_internal +//third_party/abseil-cpp/absl/debugging:demangle_rust +//third_party/abseil-cpp/absl/debugging:examine_stack +//third_party/abseil-cpp/absl/debugging:failure_signal_handler +//third_party/abseil-cpp/absl/debugging:leak_check +//third_party/abseil-cpp/absl/debugging:stacktrace +//third_party/abseil-cpp/absl/debugging:symbolize +//third_party/abseil-cpp/absl/debugging:utf8_for_code_point +//third_party/abseil-cpp/absl/functional:any_invocable +//third_party/abseil-cpp/absl/functional:bind_front +//third_party/abseil-cpp/absl/functional:function_ref +//third_party/abseil-cpp/absl/functional:overload +//third_party/abseil-cpp/absl/hash:city +//third_party/abseil-cpp/absl/hash:hash +//third_party/abseil-cpp/absl/hash:weakly_mixed_integer +//third_party/abseil-cpp/absl/log/internal:append_truncated +//third_party/abseil-cpp/absl/log/internal:check_impl +//third_party/abseil-cpp/absl/log/internal:check_op +//third_party/abseil-cpp/absl/log/internal:conditions +//third_party/abseil-cpp/absl/log/internal:config +//third_party/abseil-cpp/absl/log/internal:fnmatch +//third_party/abseil-cpp/absl/log/internal:format +//third_party/abseil-cpp/absl/log/internal:globals +//third_party/abseil-cpp/absl/log/internal:log_impl +//third_party/abseil-cpp/absl/log/internal:log_message +//third_party/abseil-cpp/absl/log/internal:log_sink_set +//third_party/abseil-cpp/absl/log/internal:nullguard +//third_party/abseil-cpp/absl/log/internal:nullstream +//third_party/abseil-cpp/absl/log/internal:proto +//third_party/abseil-cpp/absl/log/internal:strip +//third_party/abseil-cpp/absl/log/internal:structured_proto +//third_party/abseil-cpp/absl/log/internal:vlog_config +//third_party/abseil-cpp/absl/log/internal:voidify +//third_party/abseil-cpp/absl/log:absl_check +//third_party/abseil-cpp/absl/log:absl_log +//third_party/abseil-cpp/absl/log:absl_vlog_is_on +//third_party/abseil-cpp/absl/log:die_if_null +//third_party/abseil-cpp/absl/log:globals +//third_party/abseil-cpp/absl/log:initialize +//third_party/abseil-cpp/absl/log:log +//third_party/abseil-cpp/absl/log:log_entry +//third_party/abseil-cpp/absl/log:log_sink +//third_party/abseil-cpp/absl/log:log_sink_registry +//third_party/abseil-cpp/absl/log:vlog_is_on +//third_party/abseil-cpp/absl/memory:memory +//third_party/abseil-cpp/absl/meta:type_traits +//third_party/abseil-cpp/absl/numeric:bits +//third_party/abseil-cpp/absl/numeric:int128 +//third_party/abseil-cpp/absl/numeric:representation +//third_party/abseil-cpp/absl/profiling:exponential_biased +//third_party/abseil-cpp/absl/profiling:sample_recorder +//third_party/abseil-cpp/absl/random/internal:distribution_caller +//third_party/abseil-cpp/absl/random/internal:entropy_pool +//third_party/abseil-cpp/absl/random/internal:fast_uniform_bits +//third_party/abseil-cpp/absl/random/internal:fastmath +//third_party/abseil-cpp/absl/random/internal:generate_real +//third_party/abseil-cpp/absl/random/internal:iostream_state_saver +//third_party/abseil-cpp/absl/random/internal:nonsecure_base +//third_party/abseil-cpp/absl/random/internal:pcg_engine +//third_party/abseil-cpp/absl/random/internal:platform +//third_party/abseil-cpp/absl/random/internal:randen +//third_party/abseil-cpp/absl/random/internal:randen_engine +//third_party/abseil-cpp/absl/random/internal:randen_hwaes +//third_party/abseil-cpp/absl/random/internal:randen_hwaes_impl +//third_party/abseil-cpp/absl/random/internal:randen_slow +//third_party/abseil-cpp/absl/random/internal:salted_seed_seq +//third_party/abseil-cpp/absl/random/internal:seed_material +//third_party/abseil-cpp/absl/random/internal:traits +//third_party/abseil-cpp/absl/random/internal:uniform_helper +//third_party/abseil-cpp/absl/random/internal:wide_multiply +//third_party/abseil-cpp/absl/random:bit_gen_ref +//third_party/abseil-cpp/absl/random:distributions +//third_party/abseil-cpp/absl/random:random +//third_party/abseil-cpp/absl/random:seed_gen_exception +//third_party/abseil-cpp/absl/random:seed_sequences +//third_party/abseil-cpp/absl/status:status +//third_party/abseil-cpp/absl/status:statusor +//third_party/abseil-cpp/absl/strings:append_and_overwrite +//third_party/abseil-cpp/absl/strings:charset +//third_party/abseil-cpp/absl/strings:cord +//third_party/abseil-cpp/absl/strings:cord_internal +//third_party/abseil-cpp/absl/strings:cordz_functions +//third_party/abseil-cpp/absl/strings:cordz_handle +//third_party/abseil-cpp/absl/strings:cordz_info +//third_party/abseil-cpp/absl/strings:cordz_statistics +//third_party/abseil-cpp/absl/strings:cordz_update_scope +//third_party/abseil-cpp/absl/strings:cordz_update_tracker +//third_party/abseil-cpp/absl/strings:has_ostream_operator +//third_party/abseil-cpp/absl/strings:internal +//third_party/abseil-cpp/absl/strings:resize_and_overwrite +//third_party/abseil-cpp/absl/strings:str_format +//third_party/abseil-cpp/absl/strings:str_format_internal +//third_party/abseil-cpp/absl/strings:string_view +//third_party/abseil-cpp/absl/strings:strings +//third_party/abseil-cpp/absl/synchronization:graphcycles_internal +//third_party/abseil-cpp/absl/synchronization:kernel_timeout_internal +//third_party/abseil-cpp/absl/synchronization:synchronization +//third_party/abseil-cpp/absl/time/internal/cctz:civil_time +//third_party/abseil-cpp/absl/time/internal/cctz:time_zone +//third_party/abseil-cpp/absl/time:clock_interface +//third_party/abseil-cpp/absl/time:time +//third_party/abseil-cpp/absl/types:compare +//third_party/abseil-cpp/absl/types:optional +//third_party/abseil-cpp/absl/types:optional_ref +//third_party/abseil-cpp/absl/types:source_location +//third_party/abseil-cpp/absl/types:span +//third_party/abseil-cpp/absl/types:variant +//third_party/abseil-cpp/absl/utility:utility +//third_party/abseil-cpp/absl/base:exception_testing +//third_party/abseil-cpp/absl/base:iterator_traits_test_helper +//third_party/abseil-cpp/absl/container:hash_generator_testing +//third_party/abseil-cpp/absl/container:hash_policy_testing +//third_party/abseil-cpp/absl/container:test_instance_tracker +//third_party/abseil-cpp/absl/container:unordered_map_constructor_test +//third_party/abseil-cpp/absl/container:unordered_map_lookup_test +//third_party/abseil-cpp/absl/container:unordered_map_members_test +//third_party/abseil-cpp/absl/container:unordered_map_modifiers_test +//third_party/abseil-cpp/absl/container:unordered_set_constructor_test +//third_party/abseil-cpp/absl/container:unordered_set_lookup_test +//third_party/abseil-cpp/absl/container:unordered_set_members_test +//third_party/abseil-cpp/absl/container:unordered_set_modifiers_test +//third_party/abseil-cpp/absl/flags:absl_flags_config +//third_party/abseil-cpp/absl/flags:config +//third_party/abseil-cpp/absl/flags:flag +//third_party/abseil-cpp/absl/flags:marshalling +//third_party/abseil-cpp/absl/flags:parse +//third_party/abseil-cpp/absl/flags:program_name +//third_party/abseil-cpp/absl/flags:reflection +//third_party/abseil-cpp/absl/flags:usage +//third_party/abseil-cpp/absl/hash:hash_testing +//third_party/abseil-cpp/absl/log/internal:container +//third_party/abseil-cpp/absl/log/internal:flags +//third_party/abseil-cpp/absl/log/internal:structured +//third_party/abseil-cpp/absl/log/internal:test_actions +//third_party/abseil-cpp/absl/log/internal:test_helpers +//third_party/abseil-cpp/absl/log/internal:test_matchers +//third_party/abseil-cpp/absl/log:check +//third_party/abseil-cpp/absl/meta:requires +//third_party/abseil-cpp/absl/random/internal:distribution_test_util +//third_party/abseil-cpp/absl/random/internal:mock_helpers +//third_party/abseil-cpp/absl/random/internal:mock_overload_set +//third_party/abseil-cpp/absl/random/internal:mock_validators +//third_party/abseil-cpp/absl/random:mocking_bit_gen +//third_party/abseil-cpp/absl/strings:cord_test_helpers +//third_party/abseil-cpp/absl/synchronization:thread_pool +//third_party/abseil-cpp/absl/types:any diff --git a/ports/webrtc/build-0001-drop-module-deps-from-toolchain-invocations.patch b/ports/webrtc/build-0001-drop-module-deps-from-toolchain-invocations.patch new file mode 100644 index 00000000000000..a98a86ea7a2861 --- /dev/null +++ b/ports/webrtc/build-0001-drop-module-deps-from-toolchain-invocations.patch @@ -0,0 +1,66 @@ +diff --git a/toolchain/apple/toolchain.gni b/toolchain/apple/toolchain.gni +index dd2722015..f6b7a4486 100644 +--- a/toolchain/apple/toolchain.gni ++++ b/toolchain/apple/toolchain.gni +@@ -470,7 +470,7 @@ template("single_apple_toolchain") { + tool("cxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" +- command = "$coverage_wrapper$cxx $md -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} {{module_deps_no_self}} -c {{source}} -o {{output}}" ++ command = "$coverage_wrapper$cxx $md -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CXX {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] +@@ -482,7 +482,7 @@ template("single_apple_toolchain") { + + # Module file doesn't need coverage instrumentation because module files + # represent interfaces rather than implementations. +- command = "$cxx -MD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} {{module_deps_no_self}} -fmodule-name={{cc_module_name}} -c -x c++ -Xclang -emit-module {{source}} -o {{output}}" ++ command = "this-should-never-run" + depsformat = "gcc" + description = "CXX_MODULE {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.pcm" ] +diff --git a/toolchain/gcc_toolchain.gni b/toolchain/gcc_toolchain.gni +index a22678bb1..eb5db24c9 100644 +--- a/toolchain/gcc_toolchain.gni ++++ b/toolchain/gcc_toolchain.gni +@@ -332,7 +332,7 @@ template("single_gcc_toolchain") { + tool("cxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" +- command = "$coverage_wrapper$cxx $md -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} {{module_deps_no_self}} -c {{source}} -o {{output}}" ++ command = "$coverage_wrapper$cxx $md -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CXX {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] +@@ -343,7 +343,7 @@ template("single_gcc_toolchain") { + + # Module file doesn't need coverage instrumentation because module files + # represent interfaces rather than implementations. +- command = "$cxx -MD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} {{module_deps_no_self}} -fmodule-name={{cc_module_name}} -x c++ -Xclang -emit-module -c {{source}} -o {{output}}" ++ command = "this-should-never-run" + depsformat = "gcc" + description = "CXX_MODULE {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.pcm" ] +diff --git a/toolchain/win/toolchain.gni b/toolchain/win/toolchain.gni +index 8d0b87816..dbefdda0b 100644 +--- a/toolchain/win/toolchain.gni ++++ b/toolchain/win/toolchain.gni +@@ -239,7 +239,7 @@ template("msvc_toolchain") { + depsformat = "msvc" + description = "CXX_MODULE {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.pcm" ] +- command = "$env_wrapper$cl /Fo{{output}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} {{module_deps_no_self}} /Fd\"$pdbname\" -fmodule-name={{cc_module_name}} /c -x c++ -Xclang -emit-module {{source}}" ++ command = "this-should-never-run" + } + + tool("cxx") { +@@ -255,7 +255,7 @@ template("msvc_toolchain") { + + # Note that the code coverage wrapper scripts assumes that {{source}} + # comes immediately after /c. +- command = "$coverage_wrapper$env_wrapper$cl /c {{source}} /Fo{{output}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} /TP {{cflags_cc}} {{module_deps_no_self}} /Fd\"$pdbname\"" ++ command = "$coverage_wrapper$env_wrapper$cl /c {{source}} /Fo{{output}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} /TP {{cflags_cc}} /Fd\"$pdbname\"" + } + + tool("rc") { diff --git a/ports/webrtc/build-0002-fix-apple-arflags-usage.patch b/ports/webrtc/build-0002-fix-apple-arflags-usage.patch new file mode 100644 index 00000000000000..c98909cc194419 --- /dev/null +++ b/ports/webrtc/build-0002-fix-apple-arflags-usage.patch @@ -0,0 +1,18 @@ +diff --git a/toolchain/apple/toolchain.gni b/toolchain/apple/toolchain.gni +index dd2722015..122773617 100644 +--- a/toolchain/apple/toolchain.gni ++++ b/toolchain/apple/toolchain.gni +@@ -528,11 +528,11 @@ template("single_apple_toolchain") { + + # Apple's libtool fails if the response file is empty (e.g., for empty targets). + # We work around this by writing a dummy archive with the ar magic number. +- command = "rm -f {{output}} && if [ -s \"$rspfile\" ]; then TOOL_VERSION=${tool_versions.filter_libtool} $python_path $script $libtool -static -D {{arflags}} -o {{output}} @$rspfile; else printf '!\n' > {{output}}; fi" ++ command = "rm -f {{output}} && if [ -s \"$rspfile\" ]; then TOOL_VERSION=${tool_versions.filter_libtool} $python_path $script $libtool -static {{arflags}} -o {{output}} @$rspfile; else printf '!\n' > {{output}}; fi" + description = "LIBTOOL-STATIC {{output}}" + } else { + ar = "${prefix}llvm-ar" +- command = "\"$ar\" {{arflags}} -r -c -s -D {{output}} @$rspfile" ++ command = "\"$ar\" {{arflags}} -r -c -s {{output}} @$rspfile" + + # Remove the output file first so that ar doesn't try to modify the + # existing file. diff --git a/ports/webrtc/build-0004-disable-sanitize-c-array-bounds.patch b/ports/webrtc/build-0004-disable-sanitize-c-array-bounds.patch new file mode 100644 index 00000000000000..bf79d0c574fefc --- /dev/null +++ b/ports/webrtc/build-0004-disable-sanitize-c-array-bounds.patch @@ -0,0 +1,26 @@ +--- a/config/compiler/BUILD.gn ++++ b/config/compiler/BUILD.gn +@@ -1901,22 +1901,7 @@ + # See also: https://crbug.com/40891132#comment10 + config("sanitize_c_array_bounds") { + if (!is_ubsan && is_clang && !(is_asan && target_cpu == "x86")) { +- cflags = [ +- "-fsanitize=array-bounds", +- "-fsanitize-trap=array-bounds", +- +- # Some code users feature detection to determine if UBSAN (or any +- # sanitizer) is enabled, they then do expensive debug like operations. We +- # want to suppress this behaviour since we want to keep performance costs +- # as low as possible while having these checks. +- "-fsanitize-ignore-for-ubsan-feature=array-bounds", +- +- # Because we've enabled array-bounds sanitizing we also want to suppress +- # the related warning about "unsafe-buffer-usage-in-static-sized-array", +- # since we know that the array bounds sanitizing will catch any out-of- +- # bounds accesses. +- "-Wno-unsafe-buffer-usage-in-static-sized-array", +- ] ++ cflags = [] + } + } + diff --git a/ports/webrtc/build-0005-disable-sanitize-return.patch b/ports/webrtc/build-0005-disable-sanitize-return.patch new file mode 100644 index 00000000000000..431a575f68b964 --- /dev/null +++ b/ports/webrtc/build-0005-disable-sanitize-return.patch @@ -0,0 +1,20 @@ +--- a/config/compiler/BUILD.gn ++++ b/config/compiler/BUILD.gn +@@ -1925,16 +1925,7 @@ + # `NOTREACHED()` at the end of such functions. + config("sanitize_return") { + if (!is_ubsan && is_clang) { +- cflags = [ +- "-fsanitize=return", +- "-fsanitize-trap=return", +- +- # Some code users feature detection to determine if UBSAN (or any +- # sanitizer) is enabled, they then do expensive debug like operations. We +- # want to suppress this behaviour since we want to keep performance costs +- # as low as possible while having these checks. +- "-fsanitize-ignore-for-ubsan-feature=return", +- ] ++ cflags = [] + } + } + diff --git a/ports/webrtc/build-0006-skip-local-vs-debugger-copy.patch b/ports/webrtc/build-0006-skip-local-vs-debugger-copy.patch new file mode 100644 index 00000000000000..a0ded04c5f07ab --- /dev/null +++ b/ports/webrtc/build-0006-skip-local-vs-debugger-copy.patch @@ -0,0 +1,19 @@ +diff --git a/vs_toolchain.py b/vs_toolchain.py +index 5d83b6c..d82417b 100644 +--- a/vs_toolchain.py ++++ b/vs_toolchain.py +@@ -468,7 +468,10 @@ def _CopyDebugger(target_dir, target_cpu): + debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True), + ('symsrv.dll', True)] + for debug_file, is_optional in debug_files: +- full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file) ++ full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file) ++ local_toolchain = not bool( ++ int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')) ++ ) + if not os.path.exists(full_path): +- if is_optional: ++ if is_optional or local_toolchain: + continue + else: + raise Exception('%s not found in "%s"\r\nYou must install ' diff --git a/ports/webrtc/build-0007-fix-windows-pdb-commands.patch b/ports/webrtc/build-0007-fix-windows-pdb-commands.patch new file mode 100644 index 00000000000000..d4b42a757c8e9f --- /dev/null +++ b/ports/webrtc/build-0007-fix-windows-pdb-commands.patch @@ -0,0 +1,42 @@ +diff --git a/toolchain/win/toolchain.gni b/toolchain/win/toolchain.gni +index c39430d6d..2c9a5e034 100644 +--- a/toolchain/win/toolchain.gni ++++ b/toolchain/win/toolchain.gni +@@ -220,7 +220,6 @@ template("msvc_toolchain") { + + tool("cc") { + precompiled_header_type = "msvc" +- pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb" + + # Label names may have spaces in them so the pdbname must be quoted. The + # source and output don't need to be quoted because GN knows they're a +@@ -231,11 +230,10 @@ template("msvc_toolchain") { + + # Note that the code coverage wrapper scripts assumes that {{source}} + # comes immediately after /c. +- command = "$coverage_wrapper$env_wrapper$cl /c {{source}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /Fo{{output}} /Fd\"$pdbname\"" ++ command = "$coverage_wrapper$env_wrapper$cl /c {{source}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /Fo{{output}} /Fd\"{{target_out_dir}}/{{label_name}}_c.pdb\"" + } + + tool("cxx_module") { +- pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb" + depsformat = "msvc" + description = "CXX_MODULE {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.pcm" ] +@@ -246,7 +244,6 @@ template("msvc_toolchain") { + precompiled_header_type = "msvc" + + # The PDB name needs to be different between C and C++ compiled files. +- pdbname = "{{target_out_dir}}/{{label_name}}_cc.pdb" + + # See comment in CC tool about quoting. + depsformat = "msvc" +@@ -255,7 +252,7 @@ template("msvc_toolchain") { + + # Note that the code coverage wrapper scripts assumes that {{source}} + # comes immediately after /c. +- command = "$coverage_wrapper$env_wrapper$cl /c {{source}} /Fo{{output}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} /TP {{cflags_cc}} /Fd\"$pdbname\"" ++ command = "$coverage_wrapper$env_wrapper$cl /c {{source}} /Fo{{output}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} /TP {{cflags_cc}} /Fd\"{{target_out_dir}}/{{label_name}}_cc.pdb\"" + } + + tool("rc") { diff --git a/ports/webrtc/build-0008-disable-crel-on-linux-arm64.patch b/ports/webrtc/build-0008-disable-crel-on-linux-arm64.patch new file mode 100644 index 00000000000000..fae7cab1a07b34 --- /dev/null +++ b/ports/webrtc/build-0008-disable-crel-on-linux-arm64.patch @@ -0,0 +1,13 @@ +diff --git a/config/compiler/BUILD.gn b/config/compiler/BUILD.gn +index 005406136..5f01932dc 100644 +--- a/config/compiler/BUILD.gn ++++ b/config/compiler/BUILD.gn +@@ -673,7 +673,7 @@ config("compiler") { + # TODO(crbug.com/376278218): This causes segfault on Linux ARM builds. + # It also causes segfault on Linux s390x: + # https://github.com/llvm/llvm-project/issues/149511 +- if (is_linux && use_lld && current_cpu != "arm" && current_cpu != "s390x") { ++ if (false) { + cflags += [ "-Wa,--crel,--allow-experimental-crel" ] + } + } diff --git a/ports/webrtc/generate_external_absl.py b/ports/webrtc/generate_external_absl.py new file mode 100644 index 00000000000000..77d31f576d4dba --- /dev/null +++ b/ports/webrtc/generate_external_absl.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import collections +import dataclasses +import pathlib +import re +import shutil + +ROOT_TARGETS = [ + "absl", + "absl_component_deps", + "absl_full", + "absl_full_deps", + "test_support", +] + +FORBIDDEN_IMPORTED_LINK_LIBRARIES = ("abseil_test_dll", ) + + +@dataclasses.dataclass(frozen=True) +class ImportedCMakeTargets: + all_targets: tuple[str, ...] + static_targets: tuple[str, ...] + interface_targets: tuple[str, ...] + + +@dataclasses.dataclass(frozen=True) +class AbslGenerationPlan: + root_targets: tuple[str, ...] + grouped_targets: tuple[tuple[str, tuple[str, ...]], ...] + cmake_targets: ImportedCMakeTargets + static_libs: tuple[str, ...] + + +def build_imported_cmake_targets( + known_targets: dict[str, str]) -> ImportedCMakeTargets: + return ImportedCMakeTargets( + all_targets=tuple(sorted(known_targets)), + static_targets=tuple(target + for target, kind in sorted(known_targets.items()) + if kind == "STATIC"), + interface_targets=tuple( + target for target, kind in sorted(known_targets.items()) + if kind == "INTERFACE"), + ) + + +def make_generation_plan( + root_targets: list[str], + grouped_targets: dict[str, list[str]], + known_targets: dict[str, str], +) -> AbslGenerationPlan: + unexpected_root_targets = sorted(set(root_targets) - set(ROOT_TARGETS)) + if unexpected_root_targets: + raise ValueError("Unexpected root absl labels: " + + ", ".join(unexpected_root_targets)) + imported_targets = build_imported_cmake_targets(known_targets) + return AbslGenerationPlan( + root_targets=tuple(ROOT_TARGETS), + grouped_targets=tuple( + (path_part, tuple(sorted(set(targets)))) + for path_part, targets in sorted(grouped_targets.items())), + cmake_targets=imported_targets, + static_libs=tuple( + cmake_target_to_gn_lib_name(target) + for target in imported_targets.static_targets), + ) + + +def split_manifest_labels( + labels: list[str]) -> tuple[list[str], dict[str, list[str]]]: + root_targets = [] + grouped_targets: dict[str, list[str]] = collections.defaultdict(list) + for label in labels: + path_part, target = split_label(label) + if path_part: + grouped_targets[path_part].append(target) + else: + root_targets.append(target) + return root_targets, grouped_targets + + +def read_labels(path: pathlib.Path) -> list[str]: + labels = [] + for line in path.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + labels.append(line) + return sorted(set(labels)) + + +def split_label(label: str) -> tuple[str, str]: + if not label.startswith("//third_party/abseil-cpp"): + raise ValueError(f"Invalid absl label prefix: {label}") + body = label[len("//third_party/abseil-cpp"):] + if ":" not in body: + raise ValueError(f"Missing target separator in absl label: {label}") + path_part, target = body.split(":", 1) + return path_part.lstrip("/"), target + + +def write_absl_gni(path: pathlib.Path) -> None: + path.write_text(render_absl_gni()) + + +def render_absl_gni() -> str: + return """declare_args() { + absl_build_tests = false +} + +template("absl_source_set") { + source_set(target_name) { + forward_variables_from(invoker, "*") + if (!defined(public_configs)) { + public_configs = [] + } + public_configs += [ + "//third_party/abseil-cpp:absl_include_config", + "//third_party/abseil-cpp:absl_define_config", + ] + if (!defined(visibility)) { + visibility = [ "*" ] + } + } +} + +template("absl_test") { + source_set(target_name) { + not_needed(invoker, "*") + } +} +""" + + +def write_root_build( + path: pathlib.Path, + include_root: str, + lib_root: str, + static_libs: list[str], + generated_targets: list[tuple[str, str]], +) -> None: + path.write_text( + render_root_build(include_root, lib_root, static_libs, + generated_targets)) + + +def render_root_build( + include_root: str, + lib_root: str, + static_libs: list[str], + generated_targets: list[tuple[str, str]], +) -> str: + lib_lines = "\n".join(f' "{libname}",' for libname in static_libs) + generated_dep_lines = "\n".join(f' "{rel}",' for rel in sorted({ + f"//third_party/abseil-cpp/{path_part}:{target}" + for path_part, target in generated_targets + })) + root_target_blocks = "\n\n".join(f"""source_set("{target}") {{ + visibility = [ "*" ] + public_configs = [ + ":absl_include_config", + ":absl_define_config", + ":absl_all_link", + ] + public_deps = [ ":_all_generated_absl" ] +}}""" for target in ROOT_TARGETS) + + return f"""config("absl_include_config") {{ + include_dirs = [ "{include_root}" ] +}} + +config("absl_define_config") {{ + defines = [ "ABSL_ALLOCATOR_NOTHROW=1" ] +}} + +config("absl_all_link") {{ + lib_dirs = [ "{lib_root}" ] + libs = [ +{lib_lines} + ] +}} + +config("absl_default_cflags_cc") {{ +}} + +config("absl_test_config") {{ +}} + +group("_all_generated_absl") {{ + public_deps = [ +{generated_dep_lines} + ] +}} + +{root_target_blocks} +""" + + +def write_subdir_build(path: pathlib.Path, targets: list[str]) -> None: + path.write_text(render_subdir_build(targets)) + + +def render_subdir_build(targets: list[str]) -> str: + return "\n\n".join(f"""source_set("{target}") {{ + visibility = [ "*" ] + public_configs = [ + "//third_party/abseil-cpp:absl_include_config", + "//third_party/abseil-cpp:absl_define_config", + "//third_party/abseil-cpp:absl_all_link", + ] +}}""" for target in targets) + + +def should_exclude_imported_target(cmake_body: str) -> bool: + return any(forbidden_link_library in cmake_body + for forbidden_link_library in FORBIDDEN_IMPORTED_LINK_LIBRARIES) + + +def read_cmake_targets(path: pathlib.Path) -> dict[str, str]: + text = path.read_text() + targets: dict[str, str] = {} + for match in re.finditer( + r"add_library\((absl::[^ )]+) (STATIC|INTERFACE) IMPORTED\)", + text): + name, kind = match.groups() + properties_match = re.search( + rf"set_target_properties\({re.escape(name)} PROPERTIES\n(.*?)\n\)", + text, + re.S, + ) + if properties_match and should_exclude_imported_target( + properties_match.group(1)): + continue + targets[name] = kind + if not targets: + raise ValueError( + f"No imported absl targets found in CMake exports: {path}") + return targets + + +def cmake_target_to_gn_lib_name(target: str) -> str: + return f"absl_{target.split('::', 1)[1]}" + + +def render_cmake_targets(known_targets: dict[str, str]) -> str: + imported_targets = build_imported_cmake_targets(known_targets) + return render_cmake_targets_from_imported_targets(imported_targets) + + +def render_cmake_targets_from_imported_targets( + imported_targets: ImportedCMakeTargets) -> str: + lines = ["set(WEBRTC_ABSL_IMPORTED_TARGETS"] + lines.extend(f" {target}" for target in imported_targets.all_targets) + lines.append(")") + lines.append("") + lines.append("set(WEBRTC_ABSL_STATIC_IMPORTED_TARGETS") + lines.extend(f" {target}" for target in imported_targets.static_targets) + lines.append(")") + lines.append("") + lines.append("set(WEBRTC_ABSL_STATIC_ARCHIVES") + lines.extend(f" $" + for target in imported_targets.static_targets) + lines.append(")") + lines.append("") + lines.append("set(WEBRTC_ABSL_INTERFACE_IMPORTED_TARGETS") + lines.extend(f" {target}" + for target in imported_targets.interface_targets) + lines.append(")") + return "\n".join(lines) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--manifest", required=True) + parser.add_argument("--output", required=True) + parser.add_argument("--include-root", required=True) + parser.add_argument("--lib-root", required=True) + parser.add_argument("--cmake-absl-targets", required=True) + parser.add_argument("--cmake-output", required=True) + args = parser.parse_args() + + manifest = pathlib.Path(args.manifest) + output = pathlib.Path(args.output) + cmake_targets_path = pathlib.Path(args.cmake_absl_targets) + cmake_output = pathlib.Path(args.cmake_output) + + plan = make_generation_plan( + *split_manifest_labels(read_labels(manifest)), + read_cmake_targets(cmake_targets_path), + ) + + shutil.rmtree(output, ignore_errors=True) + output.mkdir(parents=True, exist_ok=True) + + generated_targets = [(path_part, target) + for path_part, targets in plan.grouped_targets + for target in targets] + cmake_output.write_text( + render_cmake_targets_from_imported_targets(plan.cmake_targets)) + write_absl_gni(output / "absl.gni") + write_root_build( + output / "BUILD.gn", + args.include_root, + args.lib_root, + list(plan.static_libs), + generated_targets, + ) + + for path_part, targets in plan.grouped_targets: + build_dir = output / path_part + build_dir.mkdir(parents=True, exist_ok=True) + write_subdir_build(build_dir / "BUILD.gn", list(targets)) + + +if __name__ == "__main__": + main() diff --git a/ports/webrtc/generate_external_absl_test.py b/ports/webrtc/generate_external_absl_test.py new file mode 100644 index 00000000000000..0a639042bfbd21 --- /dev/null +++ b/ports/webrtc/generate_external_absl_test.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import importlib.util +import pathlib +import sys +import tempfile +import unittest +from unittest import mock + +SCRIPT_PATH = pathlib.Path(__file__).with_name("generate_external_absl.py") +SPEC = importlib.util.spec_from_file_location("generate_external_absl", + SCRIPT_PATH) +assert SPEC is not None +assert SPEC.loader is not None +MODULE = importlib.util.module_from_spec(SPEC) +sys.modules[SPEC.name] = MODULE +SPEC.loader.exec_module(MODULE) + + +class GenerateExternalAbslTest(unittest.TestCase): + + def test_read_labels_filters_deduplicates_and_sorts(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + manifest = pathlib.Path(tempdir) / "absl-labels.txt" + manifest.write_text("\n".join([ + "", + " # comment", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/base:base", + "//third_party/abseil-cpp/absl/strings:strings", + ])) + + self.assertEqual( + MODULE.read_labels(manifest), + [ + "//third_party/abseil-cpp/absl/base:base", + "//third_party/abseil-cpp/absl/strings:strings", + ], + ) + + def test_build_imported_cmake_targets_normalizes_target_groups( + self) -> None: + imported_targets = MODULE.build_imported_cmake_targets({ + "absl::strings": + "STATIC", + "absl::core_headers": + "INTERFACE", + "absl::base": + "STATIC", + }) + + self.assertEqual( + imported_targets, + MODULE.ImportedCMakeTargets( + all_targets=("absl::base", "absl::core_headers", + "absl::strings"), + static_targets=("absl::base", "absl::strings"), + interface_targets=("absl::core_headers", ), + ), + ) + + def test_make_generation_plan_exposes_normalized_model(self) -> None: + plan = MODULE.make_generation_plan( + root_targets=["absl"], + grouped_targets={ + "absl/strings": ["strings", "strings"], + "absl/base": ["base"], + }, + known_targets={ + "absl::strings": "STATIC", + "absl::core_headers": "INTERFACE", + "absl::base": "STATIC", + }, + ) + + self.assertEqual(plan.root_targets, tuple(MODULE.ROOT_TARGETS)) + self.assertEqual( + plan.grouped_targets, + ( + ("absl/base", ("base", )), + ("absl/strings", ("strings", )), + ), + ) + self.assertEqual(plan.cmake_targets.static_targets, + ("absl::base", "absl::strings")) + self.assertEqual(plan.static_libs, ("absl_base", "absl_strings")) + + def test_make_generation_plan_rejects_unexpected_root_target(self) -> None: + with self.assertRaisesRegex(ValueError, "unexpected_root"): + MODULE.make_generation_plan( + root_targets=["unexpected_root"], + grouped_targets={}, + known_targets={"absl::base": "STATIC"}, + ) + + def test_split_label_returns_path_and_target(self) -> None: + self.assertEqual( + MODULE.split_label("//third_party/abseil-cpp/absl/base:base"), + ("absl/base", "base"), + ) + self.assertEqual( + MODULE.split_label("//third_party/abseil-cpp:absl"), + ("", "absl"), + ) + + def test_split_label_rejects_invalid_prefix(self) -> None: + with self.assertRaisesRegex(ValueError, "Invalid absl label prefix"): + MODULE.split_label("//third_party/not-abseil/absl/base:base") + + def test_split_label_rejects_missing_target_separator(self) -> None: + with self.assertRaisesRegex(ValueError, "Missing target separator"): + MODULE.split_label("//third_party/abseil-cpp/absl/base") + + def test_read_cmake_targets_parses_static_and_interface_targets( + self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + targets = pathlib.Path(tempdir) / "abslTargets.cmake" + targets.write_text("\n".join([ + "add_library(absl::base STATIC IMPORTED)", + "add_library(absl::core_headers INTERFACE IMPORTED)", + ])) + + self.assertEqual( + MODULE.read_cmake_targets(targets), + { + "absl::base": "STATIC", + "absl::core_headers": "INTERFACE", + }, + ) + + def test_read_cmake_targets_excludes_targets_with_forbidden_link_libraries( + self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + targets = pathlib.Path(tempdir) / "abslTargets.cmake" + targets.write_text("\n".join([ + "add_library(absl::base STATIC IMPORTED)", + "set_target_properties(absl::base PROPERTIES", + ' INTERFACE_LINK_LIBRARIES "abseil_test_dll"', + ")", + "add_library(absl::core_headers INTERFACE IMPORTED)", + ])) + + self.assertEqual( + MODULE.read_cmake_targets(targets), + { + "absl::core_headers": "INTERFACE", + }, + ) + + def test_read_cmake_targets_rejects_empty_exports(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + targets = pathlib.Path(tempdir) / "abslTargets.cmake" + targets.write_text("") + + with self.assertRaisesRegex(ValueError, + "No imported absl targets"): + MODULE.read_cmake_targets(targets) + + def test_read_cmake_targets_rejects_unparseable_exports(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + targets = pathlib.Path(tempdir) / "abslTargets.cmake" + targets.write_text("set(not_a_target value)\n") + + with self.assertRaisesRegex(ValueError, + "No imported absl targets"): + MODULE.read_cmake_targets(targets) + + def test_render_cmake_targets_preserves_static_lib_mapping(self) -> None: + known_targets = { + "absl::base": "STATIC", + "absl::core_headers": "INTERFACE", + "absl::strings": "STATIC", + } + + imported_targets = MODULE.build_imported_cmake_targets(known_targets) + static_libs = [ + MODULE.cmake_target_to_gn_lib_name(target) + for target in imported_targets.static_targets + ] + text = MODULE.render_cmake_targets(known_targets) + + self.assertEqual(static_libs, ["absl_base", "absl_strings"]) + self.assertIn("set(WEBRTC_ABSL_IMPORTED_TARGETS", text) + self.assertIn(" absl::base", text) + self.assertIn(" absl::core_headers", text) + self.assertIn("set(WEBRTC_ABSL_STATIC_IMPORTED_TARGETS", text) + self.assertIn(" absl::strings", text) + self.assertIn("$", text) + self.assertIn("set(WEBRTC_ABSL_INTERFACE_IMPORTED_TARGETS", text) + + def test_render_absl_gni_contains_templates(self) -> None: + text = MODULE.render_absl_gni() + + self.assertIn('declare_args() {', text) + self.assertIn('template("absl_source_set")', text) + self.assertIn('template("absl_test")', text) + + def test_render_root_build_includes_configs_and_generated_targets( + self) -> None: + text = MODULE.render_root_build( + include_root="/test/include", + lib_root="/test/lib", + static_libs=["absl_base", "absl_strings"], + generated_targets=[("absl/base", "base"), + ("absl/strings", "strings")], + ) + + self.assertIn('include_dirs = [ "/test/include" ]', text) + self.assertIn('lib_dirs = [ "/test/lib" ]', text) + self.assertIn('"//third_party/abseil-cpp/absl/base:base"', text) + self.assertIn('source_set("absl") {', text) + + def test_render_subdir_build_includes_targets_and_configs(self) -> None: + text = MODULE.render_subdir_build(["base", "core_headers"]) + + self.assertIn('source_set("base") {', text) + self.assertIn('source_set("core_headers") {', text) + self.assertIn('"//third_party/abseil-cpp:absl_all_link"', text) + + def test_render_cmake_targets_includes_imported_target_lists(self) -> None: + text = MODULE.render_cmake_targets({ + "absl::base": "STATIC", + "absl::core_headers": "INTERFACE", + "absl::strings": "STATIC", + }) + + self.assertIn("set(WEBRTC_ABSL_IMPORTED_TARGETS", text) + self.assertIn(" absl::core_headers", text) + self.assertIn("set(WEBRTC_ABSL_STATIC_ARCHIVES", text) + self.assertIn("$", text) + + def test_render_cmake_targets_from_imported_targets_uses_plan_model( + self) -> None: + text = MODULE.render_cmake_targets_from_imported_targets( + MODULE.ImportedCMakeTargets( + all_targets=("absl::base", "absl::core_headers", + "absl::strings"), + static_targets=("absl::base", "absl::strings"), + interface_targets=("absl::core_headers", ), + )) + + self.assertIn("set(WEBRTC_ABSL_IMPORTED_TARGETS", text) + self.assertIn(" absl::core_headers", text) + self.assertIn("$", text) + + def test_cmake_target_to_gn_lib_name_uses_current_prefix_mapping( + self) -> None: + self.assertEqual(MODULE.cmake_target_to_gn_lib_name("absl::base"), + "absl_base") + self.assertEqual( + MODULE.cmake_target_to_gn_lib_name("absl::strings_internal"), + "absl_strings_internal", + ) + + def test_main_generates_expected_files(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = pathlib.Path(tempdir) + manifest = root / "absl-labels.txt" + cmake_targets = root / "abslTargets.cmake" + output = root / "abseil-cpp" + cmake_output = root / "webrtc-absl-targets.cmake" + + manifest.write_text("\n".join([ + "//third_party/abseil-cpp:absl", + "//third_party/abseil-cpp/absl/base:base", + "//third_party/abseil-cpp/absl/strings:strings", + ])) + cmake_targets.write_text("\n".join([ + "add_library(absl::base STATIC IMPORTED)", + "add_library(absl::strings STATIC IMPORTED)", + "add_library(absl::core_headers INTERFACE IMPORTED)", + ])) + + argv = [ + "generate_external_absl.py", + "--manifest", + str(manifest), + "--output", + str(output), + "--include-root", + "/test/include", + "--lib-root", + "/test/lib", + "--cmake-absl-targets", + str(cmake_targets), + "--cmake-output", + str(cmake_output), + ] + with mock.patch("sys.argv", argv): + MODULE.main() + + absl_gni = (output / "absl.gni").read_text() + root_build = (output / "BUILD.gn").read_text() + base_build = (output / "absl" / "base" / "BUILD.gn").read_text() + generated_cmake = cmake_output.read_text() + + self.assertIn('template("absl_source_set")', absl_gni) + self.assertIn('include_dirs = [ "/test/include" ]', root_build) + self.assertIn('lib_dirs = [ "/test/lib" ]', root_build) + self.assertIn('"//third_party/abseil-cpp/absl/base:base"', + root_build) + self.assertIn('source_set("absl") {', root_build) + self.assertIn('source_set("test_support") {', root_build) + self.assertIn('source_set("base") {', base_build) + self.assertIn('"//third_party/abseil-cpp:absl_all_link"', + base_build) + self.assertIn("set(WEBRTC_ABSL_STATIC_ARCHIVES", generated_cmake) + self.assertIn("$", + generated_cmake) + + def test_main_rejects_unexpected_root_label(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = pathlib.Path(tempdir) + manifest = root / "absl-labels.txt" + cmake_targets = root / "abslTargets.cmake" + output = root / "abseil-cpp" + cmake_output = root / "webrtc-absl-targets.cmake" + + manifest.write_text("\n".join( + ["//third_party/abseil-cpp:unexpected_root"])) + cmake_targets.write_text( + "add_library(absl::base STATIC IMPORTED)\n") + + argv = [ + "generate_external_absl.py", + "--manifest", + str(manifest), + "--output", + str(output), + "--include-root", + "/test/include", + "--lib-root", + "/test/lib", + "--cmake-absl-targets", + str(cmake_targets), + "--cmake-output", + str(cmake_output), + ] + with mock.patch("sys.argv", argv): + with self.assertRaisesRegex(ValueError, "unexpected_root"): + MODULE.main() + + +if __name__ == "__main__": + unittest.main() diff --git a/ports/webrtc/generate_external_third_party.py b/ports/webrtc/generate_external_third_party.py new file mode 100644 index 00000000000000..c978bef4952011 --- /dev/null +++ b/ports/webrtc/generate_external_third_party.py @@ -0,0 +1,1093 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import dataclasses +import pathlib +import shutil + +NASM_ASSEMBLE_GNI = """# Copyright 2018 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This provides the nasm_assemble() template which uses NASM to assemble +# assembly files. +# +# Files to be assembled with NASM should have an extension of .asm. +# +# Parameters +# +# nasm_flags (optional) +# [list of strings] Pass additional flags into NASM. These are appended +# to the command line. Note that the output format is already set up +# based on the current toolchain so you don't need to specify these +# things (see below). +# +# Example: nasm_flags = [ "-Wall" ] +# +# include_dirs (optional) +# [list of dir names] List of additional include dirs. Note that the +# source root and the root generated file dir is always added, just like +# our C++ build sets up. +# +# Example: include_dirs = [ "//some/other/path", target_gen_dir ] +# +# defines (optional) +# [list of strings] List of defines, as with the native code defines. +# +# Example: defines = [ "FOO", "BAR=1" ] +# +# inputs, deps, visibility (optional) +# These have the same meaning as in an action. +# +# Example +# +# nasm_assemble("my_nasm_target") { +# sources = [ +# "ultra_optimized_awesome.asm", +# ] +# include_dirs = [ "assembly_include" ] +# } + +import("//build/compiled_action.gni") +import("//build/config/ios/config.gni") +import("//build/config/ios/ios_sdk_overrides.gni") +import("//build/config/sanitizers/sanitizers.gni") +if (is_mac) { + import("//build/config/mac/mac_sdk.gni") +} + +if ((is_mac || is_ios) && (current_cpu == "x86" || current_cpu == "x64")) { + if (current_cpu == "x86") { + _nasm_flags = [ "-fmacho32" ] + } else if (current_cpu == "x64") { + _nasm_flags = [ "-fmacho64" ] + } + if (is_mac) { + _nasm_flags += [ "--macho-min-os=macos$mac_deployment_target" ] + } else if (is_ios) { + if (target_environment == "device") { + _nasm_flags += [ "--macho-min-os=ios$ios_deployment_target" ] + } else { + _nasm_flags += + [ "--macho-min-os=ios$ios_deployment_target-$target_environment" ] + } + } +} else if (is_posix || is_fuchsia) { + if (current_cpu == "x86") { + _nasm_flags = [ "-felf32" ] + } else if (current_cpu == "x64") { + _nasm_flags = [ + "-DPIC", + "-felf64", + ] + } +} else if (is_win) { + if (current_cpu == "x86") { + _nasm_flags = [ + "-DPREFIX", + "-fwin32", + ] + } else if (current_cpu == "x64") { + _nasm_flags = [ "-fwin64" ] + } +} + +if (is_win) { + asm_obj_extension = "obj" +} else { + asm_obj_extension = "o" +} + +template("nasm_assemble") { + assert(defined(invoker.sources), "Need sources defined for $target_name") + + # Only depend on NASM on x86 systems. Force compilation of .asm files for + # ARM to fail. + assert(current_cpu == "x86" || current_cpu == "x64") + + action_name = "${target_name}_action" + source_set_name = target_name + + action_foreach(action_name) { + # Only the source set can depend on this. + visibility = [ ":$source_set_name" ] + + forward_variables_from(invoker, + [ + "sources", + "inputs", + "deps", + ]) + + if (!defined(deps)) { + deps = [] + } + if (host_toolchain_is_msan) { + deps += [ "//third_party/instrumented_libs:ld-linux($host_toolchain)" ] + configs = [ + "//third_party/instrumented_libs:msan_runtime_libs($host_toolchain)", + ] + } + + script = "{tool_path}" + + args = _nasm_flags + if (defined(invoker.nasm_flags)) { + args += invoker.nasm_flags + } + + args += [ "--reproducible" ] + + if (defined(invoker.include_dirs)) { + foreach(include, invoker.include_dirs) { + args += [ "-I" + rebase_path(include, root_build_dir) + "/" ] + } + } + + args += [ + "-I./", + "-I" + rebase_path("//", root_build_dir), + "-I" + rebase_path(root_gen_dir, root_build_dir) + "/", + ] + + if (defined(invoker.defines)) { + foreach(def, invoker.defines) { + args += [ "-D$def" ] + } + } + + outputs = [ "$target_out_dir/$source_set_name/{{source_name_part}}.${asm_obj_extension}" ] + args += [ + "-MD", + rebase_path(outputs[0] + ".d", root_build_dir), + "-o", + rebase_path(outputs[0], root_build_dir), + "{{source}}", + ] + + depfile = outputs[0] + ".d" + } + + static_library(source_set_name) { + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + + if (defined(invoker.all_dependent_configs)) { + all_dependent_configs = invoker.all_dependent_configs + } + + sources = get_target_outputs(":$action_name") + public = [] + deps = [ ":$action_name" ] + } +} +""" + +LIBSRTP_OPTIONS_GNI = """declare_args() { + libsrtp_build_boringssl = false + libsrtp_ssl_root = "" +} +""" + +TESTING_TEST_GNI = """declare_args() { + tests_have_location_tags = false + use_fuzztest_wrapper = false +} + +exec_target_suffix = "" + +template("test") { + group(target_name) { + forward_variables_from(invoker, [ + "testonly", + "visibility", + "deps", + "public_deps", + "data_deps", + ]) + } +} +""" + +GOOGLETEST_BUILD_GN = """config("gtest_config") { + include_dirs = [ + "custom", + "src/googletest/include", + ] +} + +config("gmock_config") { + include_dirs = [ + "custom", + "src/googletest/include", + "src/googlemock/include", + ] +} + +source_set("gtest") { + testonly = true + public_configs = [ ":gtest_config" ] + sources = [ + "src/googletest/include/gtest/gtest.h", + "src/googletest/src/gtest.cc", + ] +} + +source_set("gtest_main") { + testonly = true + public_configs = [ ":gtest_config" ] + deps = [ ":gtest" ] + sources = [ "src/googletest/src/gtest_main.cc" ] +} + +source_set("gmock") { + testonly = true + public_configs = [ ":gmock_config" ] + deps = [ ":gtest" ] + sources = [ + "src/googlemock/include/gmock/gmock.h", + "src/googlemock/src/gmock.cc", + ] +} + +source_set("gmock_main") { + testonly = true + public_configs = [ ":gmock_config" ] + deps = [ ":gmock" ] + sources = [ "src/googlemock/src/gmock_main.cc" ] +} +""" + +NASM_BUILD_GN = "" + +GOOGLETEST_GTEST_SPI_H = """#pragma once +#define EXPECT_FATAL_FAILURE(statement, substring) do { statement; } while (0) +#define EXPECT_NONFATAL_FAILURE(statement, substring) do { statement; } while (0) +""" + +GOOGLETEST_GTEST_H = """#pragma once +#define GTEST_HAS_DEATH_TEST 0 +#define FRIEND_TEST(test_case_name, test_name) friend class test_case_name##_##test_name##_Test +#define TEST(test_case_name, test_name) static void test_case_name##_##test_name() +#define TEST_F(test_fixture, test_name) static void test_fixture##_##test_name() +#define EXPECT_TRUE(condition) do { (void)(condition); } while (0) +#define EXPECT_FALSE(condition) do { (void)(condition); } while (0) +#define EXPECT_EQ(expected, actual) do { (void)(expected); (void)(actual); } while (0) +#define EXPECT_NE(expected, actual) do { (void)(expected); (void)(actual); } while (0) +#define ASSERT_TRUE(condition) do { (void)(condition); } while (0) +#define ASSERT_FALSE(condition) do { (void)(condition); } while (0) +#define ASSERT_EQ(expected, actual) do { (void)(expected); (void)(actual); } while (0) +namespace testing { class Test {}; } +""" + +GOOGLETEST_ASSERTION_RESULT_H = """#pragma once +namespace testing { class AssertionResult {}; } +""" + +GOOGLETEST_GTEST_PROD_H = """#pragma once +#define FRIEND_TEST(test_case_name, test_name) \ + friend class test_case_name##_##test_name##_Test +""" + +TEST_MAIN_CC = """int main(int argc, char** argv) { + (void)argc; + (void)argv; + return 0; +} +""" + +LIBAOM_OPTIONS_GNI = """import("//build/config/cast.gni") +import("//build/config/chromeos/ui_mode.gni") +import("//build/config/gclient_args.gni") + +declare_args() { + enable_libaom = !(is_android && current_cpu != "arm64" && + current_cpu != "x64") && !is_castos +} +""" + +LIBAOM_BUILD_GN = """config("libaom_public_config") { + include_dirs = [ "{include_root}" ] +} + +config("libaom_config") { + include_dirs = [ "{include_root}" ] +} + +config("libaom_link") { + lib_dirs = [ "{lib_root}" ] + libs = [ "aom" ] +} + +source_set("libaom") { + public_configs = [ ":libaom_public_config" ] + configs += [ + ":libaom_config", + ":libaom_link", + ] + sources = [ + "source/libaom/aom/aom_codec.h", + "source/libaom/aom/aom_encoder.h", + "source/libaom/aom/aom_image.h", + "source/libaom/aom/aomcx.h", + ] +} +""" + +LIBAOM_AOMCX_H = """#pragma once +#include +#ifndef AOM_EFLAG_FREEZE_INTERNAL_STATE +#define AOM_EFLAG_FREEZE_INTERNAL_STATE 0 +#endif +""" + +PROTOBUF_BUILD_GN = """config("protobuf_config") { +} + +group("protobuf_lite") { + public_configs = [ ":protobuf_config" ] +} +""" + +PROTOBUF_PROTO_LIBRARY_GNI = """template("proto_library") { + group(target_name) { + forward_variables_from(invoker, [ + "testonly", + "visibility", + ]) + } +} +""" + +GENERATE_STUBS_SCRIPT = """#!/usr/bin/env python3 +raise SystemExit("generate_stubs shim should not execute in this build") +""" + +GENERATE_STUBS_RULES_GNI = """template("generate_stubs") { + group(target_name) { + forward_variables_from(invoker, [ + "testonly", + "visibility", + "deps", + "public_deps", + "data_deps", + ]) + } +} +""" + + +def make_header_forwarder(include_path: str) -> str: + return f"""#pragma once +#include <{include_path}> +""" + + +def make_trivial_group_build_gn(*group_names: str) -> str: + return "\n\n".join(f'group("{group_name}") {{\n}}' + for group_name in group_names) + "\n" + + +def make_config_only_build_gn(config_name: str, + include_dirs: list[str]) -> str: + include_lines = "\n".join(f' "{include_dir}",' + for include_dir in include_dirs) + return f"""config("{config_name}") {{ + include_dirs = [ +{include_lines} + ] +}} +""" + + +def make_linked_source_set_build_gn( + target_name: str, + include_dirs: list[str], + lib_name: str, + sources: list[str], + *, + public_config_name: str | None = None, + config_name: str | None = None, + testonly: bool = False, + deps: list[str] | None = None, + extra_configs: list[str] | None = None, +) -> str: + public_config_name = public_config_name or config_name or f"{target_name}_config" + config_name = config_name or public_config_name + deps = [] if deps is None else deps + extra_configs = [] if extra_configs is None else extra_configs + link_config_name = f"{target_name}_link" + + def render_include_block(name: str) -> str: + include_lines = "\n".join(f' "{include_dir}",' + for include_dir in include_dirs) + return f"""config("{name}") {{ + include_dirs = [ +{include_lines} + ] +}}""" + + config_blocks = [render_include_block(public_config_name)] + if config_name != public_config_name: + config_blocks.append(render_include_block(config_name)) + config_blocks.append(f"""config("{link_config_name}") {{ + lib_dirs = [ "{{lib_root}}" ] + libs = [ "{lib_name}" ] +}}""") + + source_lines = "\n".join(f' "{source}",' for source in sources) + target_lines = [f'source_set("{target_name}") {{'] + if testonly: + target_lines.append(" testonly = true") + target_lines.append(f' public_configs = [ ":{public_config_name}" ]') + if deps: + deps_line = ", ".join(f'"{dep}"' for dep in deps) + target_lines.append(f" deps = [ {deps_line} ]") + configs = [f'":{link_config_name}"'] + if config_name != public_config_name: + configs.insert(0, f'":{config_name}"') + configs.extend(f'"{config}"' for config in extra_configs) + if len(configs) == 1: + target_lines.append(f" configs += [ {configs[0]} ]") + else: + target_lines.append(" configs += [") + target_lines.extend(f" {config}," for config in configs) + target_lines.append(" ]") + target_lines.append(" sources = [") + target_lines.append(source_lines) + target_lines.append(" ]") + target_lines.append("}") + + return "\n\n".join(config_blocks + ["\n".join(target_lines)]) + "\n" + + +SPECS = { + "libsrtp": { + "relative_root": "third_party/libsrtp", + "dirs": ["include", "srtp"], + "files": { + "options.gni": + LIBSRTP_OPTIONS_GNI, + "BUILD.gn": + """import("//third_party/libsrtp/options.gni") + +config("libsrtp_config") { + include_dirs = [ "{include_root}" ] +} + +config("libsrtp_link") { + lib_dirs = [ "{lib_root}" ] + libs = [ "srtp2" ] +} + +source_set("libsrtp") { + public_configs = [ ":libsrtp_config" ] + configs += [ ":libsrtp_link" ] + sources = [ "include/srtp.h" ] +} +""", + "include/srtp.h": + """#pragma once + +#if __has_include() +#include +#else +#include +#endif +""", + }, + }, + "opus": { + "relative_root": "third_party/opus", + "dirs": ["src/include"], + "files": { + "BUILD.gn": + make_linked_source_set_build_gn( + "opus", + ["{include_root}"], + "opus", + [ + "src/include/opus.h", + "src/include/opus_defines.h", + "src/include/opus_multistream.h", + "src/include/opus_types.h", + ], + ), + "src/include/opus.h": + make_header_forwarder("opus/opus.h"), + "src/include/opus_defines.h": + make_header_forwarder("opus/opus_defines.h"), + "src/include/opus_multistream.h": + make_header_forwarder("opus/opus_multistream.h"), + "src/include/opus_types.h": + make_header_forwarder("opus/opus_types.h"), + }, + }, + "libvpx": { + "relative_root": "third_party/libvpx", + "dirs": ["source/libvpx/vpx"], + "files": { + "BUILD.gn": + make_linked_source_set_build_gn( + "libvpx", + ["source/libvpx"], + "vpx", + [ + "source/libvpx/vpx/vp8.h", + "source/libvpx/vpx/vp8cx.h", + "source/libvpx/vpx/vp8dx.h", + "source/libvpx/vpx/vpx_codec.h", + "source/libvpx/vpx/vpx_decoder.h", + "source/libvpx/vpx/vpx_encoder.h", + "source/libvpx/vpx/vpx_ext_ratectrl.h", + "source/libvpx/vpx/vpx_frame_buffer.h", + "source/libvpx/vpx/vpx_image.h", + "source/libvpx/vpx/vpx_integer.h", + ], + public_config_name="libvpx_public_config", + config_name="libvpx_config", + ), + "source/libvpx/vpx/vp8.h": + make_header_forwarder("vpx/vp8.h"), + "source/libvpx/vpx/vp8cx.h": + make_header_forwarder("vpx/vp8cx.h"), + "source/libvpx/vpx/vp8dx.h": + make_header_forwarder("vpx/vp8dx.h"), + "source/libvpx/vpx/vpx_codec.h": + make_header_forwarder("vpx/vpx_codec.h"), + "source/libvpx/vpx/vpx_decoder.h": + make_header_forwarder("vpx/vpx_decoder.h"), + "source/libvpx/vpx/vpx_encoder.h": + make_header_forwarder("vpx/vpx_encoder.h"), + "source/libvpx/vpx/vpx_ext_ratectrl.h": + make_header_forwarder("vpx/vpx_ext_ratectrl.h"), + "source/libvpx/vpx/vpx_frame_buffer.h": + make_header_forwarder("vpx/vpx_frame_buffer.h"), + "source/libvpx/vpx/vpx_image.h": + make_header_forwarder("vpx/vpx_image.h"), + "source/libvpx/vpx/vpx_integer.h": + make_header_forwarder("vpx/vpx_integer.h"), + }, + }, + "libyuv": { + "relative_root": "third_party/libyuv", + "dirs": ["include/libyuv"], + "files": { + "BUILD.gn": + make_linked_source_set_build_gn( + "libyuv", + ["include"], + "yuv", + [ + "include/libyuv/compare.h", + "include/libyuv/convert.h", + "include/libyuv/convert_from.h", + "include/libyuv/planar_functions.h", + "include/libyuv/rotate.h", + "include/libyuv/rotate_argb.h", + "include/libyuv/scale.h", + "include/libyuv/video_common.h", + ], + ), + "include/libyuv/compare.h": + make_header_forwarder("libyuv/compare.h"), + "include/libyuv/convert.h": + make_header_forwarder("libyuv/convert.h"), + "include/libyuv/convert_from.h": + make_header_forwarder("libyuv/convert_from.h"), + "include/libyuv/planar_functions.h": + make_header_forwarder("libyuv/planar_functions.h"), + "include/libyuv/rotate.h": + make_header_forwarder("libyuv/rotate.h"), + "include/libyuv/rotate_argb.h": + make_header_forwarder("libyuv/rotate_argb.h"), + "include/libyuv/scale.h": + make_header_forwarder("libyuv/scale.h"), + "include/libyuv/video_common.h": + make_header_forwarder("libyuv/video_common.h"), + }, + }, + "libaom": { + "relative_root": "third_party/libaom", + "dirs": ["source/libaom/aom"], + "files": { + "options.gni": + LIBAOM_OPTIONS_GNI, + "BUILD.gn": + LIBAOM_BUILD_GN, + "source/libaom/aom/aom_codec.h": + make_header_forwarder("aom/aom_codec.h"), + "source/libaom/aom/aom_encoder.h": + make_header_forwarder("aom/aom_encoder.h"), + "source/libaom/aom/aom_image.h": + make_header_forwarder("aom/aom_image.h"), + "source/libaom/aom/aomcx.h": + LIBAOM_AOMCX_H, + }, + }, + "jsoncpp": { + "relative_root": "third_party/jsoncpp", + "dirs": ["source/include"], + "files": { + "BUILD.gn": + make_linked_source_set_build_gn( + "jsoncpp", + ["{include_root}"], + "jsoncpp", + ["source/include/jsoncpp_shim.h"], + ), + "source/include/jsoncpp_shim.h": + "#pragma once\n", + }, + }, + "dav1d": { + "relative_root": "third_party/dav1d", + "dirs": [], + "files": { + "BUILD.gn": make_trivial_group_build_gn("dav1d"), + }, + }, + "llvm-libc": { + "relative_root": "third_party/llvm-libc", + "dirs": [], + "files": { + "BUILD.gn": make_trivial_group_build_gn("llvm-libc-shared"), + }, + }, + "protobuf": { + "relative_root": "third_party/protobuf", + "dirs": [], + "files": { + "BUILD.gn": PROTOBUF_BUILD_GN, + "proto_library.gni": PROTOBUF_PROTO_LIBRARY_GNI, + }, + }, + "pffft": { + "relative_root": "third_party/pffft", + "dirs": ["src"], + "files": { + "BUILD.gn": + make_linked_source_set_build_gn( + "pffft", + ["{include_root}"], + "pffft", + ["src/pffft.h"], + ), + "src/pffft.h": + make_header_forwarder("pffft/pffft.h"), + }, + }, + "alsa": { + "relative_root": "third_party/alsa", + "dirs": [], + "files": { + "BUILD.gn": make_config_only_build_gn("headers", + ["{include_root}"]), + }, + }, + "pulseaudio": { + "relative_root": "third_party/pulseaudio", + "dirs": [], + "files": { + "BUILD.gn": make_config_only_build_gn("headers", + ["{include_root}"]), + }, + }, + "third_party_root": { + "relative_root": "third_party", + "dirs": [], + "preserve_root": True, + "files": { + "BUILD.gn": make_trivial_group_build_gn("jpeg"), + }, + }, + "testing": { + "relative_root": "testing", + "dirs": ["gmock", "gtest"], + "files": { + "BUILD.gn": + """group("pytype_dependencies") { + testonly = true +} +""", + "test.gni": + TESTING_TEST_GNI, + "gtest/BUILD.gn": + """group("gtest") { + testonly = true +} + +group("gtest_main") { + testonly = true + public_deps = [ ":gtest" ] +} +""", + "gmock/BUILD.gn": + """group("gmock") { + testonly = true +} + +group("gmock_main") { + testonly = true + public_deps = [ ":gmock" ] +} +""", + }, + }, + "googletest": { + "relative_root": + "third_party/googletest", + "dirs": [ + "custom/gtest/internal/custom", + "src/googletest/include/gtest/internal/custom", + "src/googletest/include/gtest/internal", + "src/googletest/include/gtest", + "src/googletest/src", + "src/googlemock/include/gmock/internal/custom", + "src/googlemock/include/gmock/internal", + "src/googlemock/include/gmock", + "src/googlemock/src", + ], + "files": { + "BUILD.gn": + GOOGLETEST_BUILD_GN, + "custom/gtest/internal/custom/gtest.h": + "#pragma once\n", + "custom/gtest/internal/custom/stack_trace_getter.cc": + "namespace testing {}\n", + "custom/gtest/internal/custom/stack_trace_getter.h": + "#pragma once\n", + "custom/gtest/internal/custom/chrome_custom_temp_dir.cc": + "namespace testing {}\n", + "custom/gtest/internal/custom/chrome_custom_temp_dir.h": + "#pragma once\n", + "custom/gtest/internal/custom/gtest_port_wrapper.cc": + "namespace testing {}\n", + "src/googletest/include/gtest/gtest-assertion-result.h": + GOOGLETEST_ASSERTION_RESULT_H, + "src/googletest/include/gtest/gtest-death-test.h": + "#pragma once\n", + "src/googletest/include/gtest/gtest-matchers.h": + "#pragma once\n", + "src/googletest/include/gtest/gtest-message.h": + "#pragma once\nnamespace testing { class Message {}; }\n", + "src/googletest/include/gtest/gtest-param-test.h": + "#pragma once\n", + "src/googletest/include/gtest/gtest-printers.h": + "#pragma once\n", + "src/googletest/include/gtest/gtest-spi.h": + GOOGLETEST_GTEST_SPI_H, + "src/googletest/include/gtest/gtest-test-part.h": + "#pragma once\n", + "src/googletest/include/gtest/gtest-typed-test.h": + "#pragma once\n", + "src/googletest/include/gtest/gtest.h": + GOOGLETEST_GTEST_H, + "src/googletest/include/gtest/gtest_pred_impl.h": + "#pragma once\n", + "src/googletest/include/gtest/gtest_prod.h": + GOOGLETEST_GTEST_PROD_H, + "src/googletest/include/gtest/internal/custom/gtest-port.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/custom/gtest-printers.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/gtest-death-test-internal.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/gtest-filepath.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/gtest-internal.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/gtest-param-util.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/gtest-port-arch.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/gtest-port.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/gtest-string.h": + "#pragma once\n", + "src/googletest/include/gtest/internal/gtest-type-util.h": + "#pragma once\n", + "src/googletest/src/gtest-assertion-result.cc": + "namespace testing {}\n", + "src/googletest/src/gtest-death-test.cc": + "namespace testing {}\n", + "src/googletest/src/gtest-filepath.cc": + "namespace testing {}\n", + "src/googletest/src/gtest-internal-inl.h": + "#pragma once\n", + "src/googletest/src/gtest-matchers.cc": + "namespace testing {}\n", + "src/googletest/src/gtest-printers.cc": + "namespace testing {}\n", + "src/googletest/src/gtest-test-part.cc": + "namespace testing {}\n", + "src/googletest/src/gtest-typed-test.cc": + "namespace testing {}\n", + "src/googletest/src/gtest.cc": + "namespace testing {}\n", + "src/googletest/src/gtest_main.cc": + TEST_MAIN_CC, + "src/googlemock/include/gmock/gmock-actions.h": + "#pragma once\n", + "src/googlemock/include/gmock/gmock-cardinalities.h": + "#pragma once\n", + "src/googlemock/include/gmock/gmock-function-mocker.h": + "#pragma once\n#define MOCK_METHOD(...) \n", + "src/googlemock/include/gmock/gmock-matchers.h": + "#pragma once\n", + "src/googlemock/include/gmock/gmock-more-matchers.h": + "#pragma once\n", + "src/googlemock/include/gmock/gmock-nice-strict.h": + "#pragma once\n", + "src/googlemock/include/gmock/gmock-spec-builders.h": + "#pragma once\n", + "src/googlemock/include/gmock/gmock.h": + "#pragma once\n#include \"gmock-function-mocker.h\"\n", + "src/googlemock/include/gmock/internal/custom/gmock-generated-actions.h": + "#pragma once\n", + "src/googlemock/include/gmock/internal/custom/gmock-matchers.h": + "#pragma once\n", + "src/googlemock/include/gmock/internal/gmock-internal-utils.h": + "#pragma once\n", + "src/googlemock/include/gmock/internal/gmock-port.h": + "#pragma once\n", + "src/googlemock/include/gmock/internal/gmock-pp.h": + "#pragma once\n", + "src/googlemock/src/gmock-cardinalities.cc": + "namespace testing {}\n", + "src/googlemock/src/gmock-internal-utils.cc": + "namespace testing {}\n", + "src/googlemock/src/gmock-matchers.cc": + "namespace testing {}\n", + "src/googlemock/src/gmock-spec-builders.cc": + "namespace testing {}\n", + "src/googlemock/src/gmock.cc": + "namespace testing {}\n", + "src/googlemock/src/gmock_main.cc": + TEST_MAIN_CC, + }, + }, + "catapult": { + "relative_root": + "third_party/catapult", + "dirs": [ + "third_party/typ", + "tracing", + "tracing/tracing", + "tracing/tracing/proto", + ], + "files": { + "tracing/BUILD.gn": + """group("tracing_common") { +} + +group("convert_chart_json") { + data_deps = [ ":tracing_common" ] +} +""", + "tracing/tracing/BUILD.gn": + make_trivial_group_build_gn("histogram", "reserved_infos"), + "tracing/tracing/proto/BUILD.gn": + make_trivial_group_build_gn("histogram_proto"), + }, + }, + "tools": { + "relative_root": "tools", + "dirs": ["clang/scripts", "generate_stubs", "rust"], + "files": { + "clang/scripts/update.py": + "CLANG_REVISION = 'llvmorg-0-init'\nCLANG_SUB_REVISION = 0\n", + "generate_stubs/generate_stubs.py": + GENERATE_STUBS_SCRIPT, + "generate_stubs/rules.gni": + GENERATE_STUBS_RULES_GNI, + "rust/update_rust.py": + "RUST_REVISION = 'llvmorg-0-init'\nRUST_SUB_REVISION = 0\n", + }, + }, + "nasm": { + "relative_root": "third_party/nasm", + "dirs": [], + "files": { + "BUILD.gn": NASM_BUILD_GN, + "nasm_assemble.gni": NASM_ASSEMBLE_GNI, + }, + }, +} + + +@dataclasses.dataclass(frozen=True) +class ResolvedSpec: + dep: str + relative_root: str + dirs: tuple[str, ...] + files: dict[str, str] + copy_files: dict[str, str] + preserve_root: bool + + +@dataclasses.dataclass(frozen=True) +class PlannedFile: + relative_path: str + content: str + + +@dataclasses.dataclass(frozen=True) +class PlannedCopyFile: + relative_path: str + asset_source: pathlib.Path + + +@dataclasses.dataclass(frozen=True) +class GenerationPlan: + output_root: pathlib.Path + preserve_root: bool + directories: tuple[pathlib.Path, ...] + files: tuple[PlannedFile, ...] + copied_files: tuple[PlannedCopyFile, ...] + + +def resolve_spec( + dep: str, + specs: dict[str, dict[str, object]] | None = None) -> ResolvedSpec: + specs = SPECS if specs is None else specs + spec = specs[dep] + + if "relative_root" not in spec: + raise ValueError(f"Missing required 'relative_root' for {dep}") + if "dirs" not in spec: + raise ValueError(f"Missing required 'dirs' for {dep}") + if "files" not in spec: + raise ValueError(f"Missing required 'files' for {dep}") + + relative_root = spec["relative_root"] + dirs = spec["dirs"] + files = spec["files"] + copy_files = spec.get("copy_files", {}) + preserve_root = spec.get("preserve_root", False) + + if not isinstance(relative_root, str): + raise ValueError(f"Invalid 'relative_root' for {dep}") + if not isinstance(dirs, list): + raise ValueError(f"Invalid 'dirs' for {dep}") + if not isinstance(files, dict): + raise ValueError(f"Invalid 'files' for {dep}") + if not isinstance(copy_files, dict): + raise ValueError(f"Invalid 'copy_files' for {dep}") + if not isinstance(preserve_root, bool): + raise ValueError(f"Invalid 'preserve_root' for {dep}") + + return ResolvedSpec( + dep=dep, + relative_root=relative_root, + dirs=tuple(dirs), + files=dict(files), + copy_files=dict(copy_files), + preserve_root=preserve_root, + ) + + +def render_template(content: str, include_root: str, lib_root: str, + tool_path: str) -> str: + return (content.replace("{include_root}", include_root).replace( + "{lib_root}", lib_root).replace("{tool_path}", tool_path)) + + +def build_generation_plan( + source_root: pathlib.Path, + include_root: str, + lib_root: str, + tool_path: str, + spec: ResolvedSpec, +) -> GenerationPlan: + output_root = source_root / spec.relative_root + directories = tuple(output_root / rel_dir for rel_dir in spec.dirs) + files = tuple( + PlannedFile( + relative_path=rel_path, + content=render_template(content, include_root, lib_root, + tool_path), + ) for rel_path, content in spec.files.items()) + copied_files = tuple( + PlannedCopyFile( + relative_path=rel_path, + asset_source=pathlib.Path(__file__).resolve().parent / + asset_rel_path, + ) for rel_path, asset_rel_path in spec.copy_files.items()) + return GenerationPlan( + output_root=output_root, + preserve_root=spec.preserve_root, + directories=directories, + files=files, + copied_files=copied_files, + ) + + +def plan_generation( + source_root: pathlib.Path, + dep: str, + include_root: str, + lib_root: str, + tool_path: str, +) -> GenerationPlan: + spec = resolve_spec(dep) + return build_generation_plan(source_root, include_root, lib_root, + tool_path, spec) + + +def apply_generation_plan(plan: GenerationPlan) -> None: + output = plan.output_root + if not plan.preserve_root: + shutil.rmtree(output, ignore_errors=True) + output.mkdir(parents=True, exist_ok=True) + for directory in plan.directories: + directory.mkdir(parents=True, exist_ok=True) + for planned_file in plan.files: + path = output / planned_file.relative_path + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(planned_file.content) + for copied_file in plan.copied_files: + path = output / copied_file.relative_path + path.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(copied_file.asset_source, path) + + +def write_tree(source_root: pathlib.Path, dep: str, include_root: str, + lib_root: str, tool_path: str) -> None: + apply_generation_plan( + plan_generation(source_root, dep, include_root, lib_root, tool_path)) + + +def generate_external_dep( + source_root: pathlib.Path, + dep: str, + include_root: str, + lib_root: str, + tool_path: str, +) -> None: + apply_generation_plan( + plan_generation(source_root, dep, include_root, lib_root, tool_path)) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--source-root", required=True) + parser.add_argument("--dep", choices=sorted(SPECS), required=True) + parser.add_argument("--include-root", required=True) + parser.add_argument("--lib-root", required=True) + parser.add_argument("--tool-path", default="") + args = parser.parse_args() + + generate_external_dep( + pathlib.Path(args.source_root), + args.dep, + args.include_root, + args.lib_root, + args.tool_path, + ) + + +if __name__ == "__main__": + main() diff --git a/ports/webrtc/generate_external_third_party_test.py b/ports/webrtc/generate_external_third_party_test.py new file mode 100644 index 00000000000000..cae7a9c6be96cf --- /dev/null +++ b/ports/webrtc/generate_external_third_party_test.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import importlib.util +import pathlib +import sys +import tempfile +import unittest +from unittest import mock + +SCRIPT_PATH = pathlib.Path(__file__).with_name( + "generate_external_third_party.py") +SPEC = importlib.util.spec_from_file_location("generate_external_third_party", + SCRIPT_PATH) +assert SPEC is not None +assert SPEC.loader is not None +MODULE = importlib.util.module_from_spec(SPEC) +sys.modules[SPEC.name] = MODULE +SPEC.loader.exec_module(MODULE) + + +class GenerateExternalThirdPartyTest(unittest.TestCase): + + def test_script_has_no_lines_over_120_columns(self) -> None: + script_path = pathlib.Path(MODULE.__file__) + long_lines = [(line_number, len(line)) + for line_number, line in enumerate( + script_path.read_text().splitlines(), 1) + if len(line) > 120] + + self.assertEqual(long_lines, []) + + def test_make_header_forwarder_renders_expected_include(self) -> None: + self.assertEqual( + MODULE.make_header_forwarder("opus/opus.h"), + "#pragma once\n#include \n", + ) + + def test_make_linked_source_set_build_gn_renders_expected_sections( + self) -> None: + text = MODULE.make_linked_source_set_build_gn( + "sample", + ["include", "{include_root}"], + "samplelib", + ["include/sample.h", "src/sample.cc"], + public_config_name="sample_public_config", + config_name="sample_config", + deps=[":base"], + extra_configs=["//build:no_chromium_code"], + testonly=True, + ) + + self.assertIn('config("sample_public_config") {', text) + self.assertIn('config("sample_config") {', text) + self.assertIn('config("sample_link") {', text) + self.assertIn('lib_dirs = [ "{lib_root}" ]', text) + self.assertIn('testonly = true', text) + self.assertIn('deps = [ ":base" ]', text) + self.assertIn('"//build:no_chromium_code"', text) + self.assertIn('"src/sample.cc",', text) + + def test_resolve_spec_normalizes_optional_fields(self) -> None: + spec = MODULE.resolve_spec("libsrtp") + + self.assertEqual(spec.dep, "libsrtp") + self.assertEqual(spec.relative_root, "third_party/libsrtp") + self.assertEqual(spec.dirs, ("include", "srtp")) + self.assertFalse(spec.preserve_root) + self.assertEqual(spec.copy_files, {}) + self.assertIn("BUILD.gn", spec.files) + + def test_resolve_spec_rejects_missing_required_key(self) -> None: + with self.assertRaisesRegex(ValueError, "relative_root"): + MODULE.resolve_spec( + "custom", + specs={"custom": { + "dirs": [], + "files": {}, + }}, + ) + + def test_resolve_spec_rejects_invalid_optional_type(self) -> None: + with self.assertRaisesRegex(ValueError, "copy_files"): + MODULE.resolve_spec( + "custom", + specs={ + "custom": { + "relative_root": "third_party/custom", + "dirs": [], + "files": {}, + "copy_files": [], + } + }, + ) + + def test_build_generation_plan_normalizes_output_and_files(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + spec = MODULE.resolve_spec("libsrtp") + + plan = MODULE.build_generation_plan( + source_root, + "/test/include", + "/test/lib", + "", + spec, + ) + + self.assertEqual(plan.output_root, + source_root / "third_party" / "libsrtp") + self.assertFalse(plan.preserve_root) + self.assertIn(source_root / "third_party" / "libsrtp" / "include", + plan.directories) + build_file = next(item for item in plan.files + if item.relative_path == "BUILD.gn") + self.assertIn('include_dirs = [ "/test/include" ]', + build_file.content) + self.assertIn('lib_dirs = [ "/test/lib" ]', build_file.content) + + def test_build_generation_plan_renders_tool_path(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + spec = MODULE.resolve_spec("nasm") + + plan = MODULE.build_generation_plan( + source_root, + "/test/include", + "/test/lib", + "/tool/nasm", + spec, + ) + + build_gn = next(item for item in plan.files + if item.relative_path == "nasm_assemble.gni") + self.assertIn('script = "/tool/nasm"', build_gn.content) + self.assertIn('action_foreach(action_name) {', build_gn.content) + self.assertNotIn('compiled_action_foreach(action_name)', build_gn.content) + + def test_build_generation_plan_renders_windows_tool_path(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + spec = MODULE.resolve_spec("nasm") + + plan = MODULE.build_generation_plan( + source_root, + "/test/include", + "/test/lib", + "/tool/nasm.exe", + spec, + ) + + build_gn = next(item for item in plan.files + if item.relative_path == "nasm_assemble.gni") + self.assertIn('script = "/tool/nasm.exe"', build_gn.content) + self.assertNotIn('compiled_action_foreach(action_name)', build_gn.content) + + def test_build_generation_plan_preserves_root_flag(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + spec = MODULE.resolve_spec("third_party_root") + + plan = MODULE.build_generation_plan( + source_root, + "/test/include", + "/test/lib", + "", + spec, + ) + + self.assertEqual(plan.output_root, source_root / "third_party") + self.assertTrue(plan.preserve_root) + self.assertEqual(plan.directories, ()) + + def test_plan_generation_uses_resolved_spec(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + + plan = MODULE.plan_generation( + source_root, + "testing", + "/test/include", + "/test/lib", + "", + ) + + self.assertEqual(plan.output_root, source_root / "testing") + self.assertIn(source_root / "testing" / "gmock", plan.directories) + self.assertTrue( + any(item.relative_path == "gtest/BUILD.gn" + for item in plan.files)) + + def test_plan_generation_exposes_copied_files_tuple(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + + plan = MODULE.plan_generation( + source_root, + "libsrtp", + "/test/include", + "/test/lib", + "", + ) + + self.assertEqual(plan.copied_files, ()) + + def test_apply_generation_plan_writes_planned_files(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + plan = MODULE.plan_generation( + source_root, + "libsrtp", + "/test/include", + "/test/lib", + "", + ) + + MODULE.apply_generation_plan(plan) + + dep_root = source_root / "third_party" / "libsrtp" + self.assertTrue((dep_root / "include").is_dir()) + self.assertIn('source_set("libsrtp")', + (dep_root / "BUILD.gn").read_text()) + + def test_generate_external_dep_applies_plan(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + + MODULE.generate_external_dep( + source_root, + "nasm", + "/test/include", + "/test/lib", + "/tool/nasm", + ) + + dep_root = source_root / "third_party" / "nasm" + self.assertIn('script = "/tool/nasm"', + (dep_root / "nasm_assemble.gni").read_text()) + + def test_write_tree_generates_libsrtp_with_substitution(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + + MODULE.write_tree(source_root, "libsrtp", "/test/include", + "/test/lib", "") + + dep_root = source_root / "third_party" / "libsrtp" + self.assertTrue((dep_root / "include").is_dir()) + self.assertTrue((dep_root / "srtp").is_dir()) + build_gn = (dep_root / "BUILD.gn").read_text() + self.assertIn('include_dirs = [ "/test/include" ]', build_gn) + self.assertIn('lib_dirs = [ "/test/lib" ]', build_gn) + self.assertIn('libs = [ "srtp2" ]', build_gn) + header = (dep_root / "include" / "srtp.h").read_text() + self.assertIn("#if __has_include()", header) + + def test_write_tree_preserves_existing_root_when_requested(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + dep_root = source_root / "third_party" + dep_root.mkdir(parents=True, exist_ok=True) + marker = dep_root / "keep.txt" + marker.write_text("keep") + + MODULE.write_tree(source_root, "third_party_root", "/test/include", + "/test/lib", "") + + self.assertEqual(marker.read_text(), "keep") + self.assertEqual((dep_root / "BUILD.gn").read_text(), + 'group("jpeg") {\n}\n') + + def test_write_tree_renders_nasm_tool_path(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + + MODULE.write_tree(source_root, "nasm", "/test/include", + "/test/lib", "/tool/nasm") + + dep_root = source_root / "third_party" / "nasm" + assemble_gni = (dep_root / "nasm_assemble.gni").read_text() + self.assertIn('script = "/tool/nasm"', assemble_gni) + self.assertNotIn('compiled_action_foreach(action_name)', assemble_gni) + self.assertEqual("", (dep_root / "BUILD.gn").read_text()) + + def test_write_tree_generates_nested_testing_tree(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + + MODULE.write_tree(source_root, "testing", "/test/include", + "/test/lib", "") + + dep_root = source_root / "testing" + self.assertTrue((dep_root / "gmock").is_dir()) + self.assertTrue((dep_root / "gtest").is_dir()) + self.assertIn('group("pytype_dependencies")', + (dep_root / "BUILD.gn").read_text()) + self.assertIn('group("gtest")', + (dep_root / "gtest" / "BUILD.gn").read_text()) + self.assertIn('group("gmock")', + (dep_root / "gmock" / "BUILD.gn").read_text()) + + def test_main_generates_representative_output_tree(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + source_root = pathlib.Path(tempdir) + argv = [ + "generate_external_third_party.py", + "--source-root", + str(source_root), + "--dep", + "libsrtp", + "--include-root", + "/test/include", + "--lib-root", + "/test/lib", + ] + with mock.patch("sys.argv", argv): + MODULE.main() + + dep_root = source_root / "third_party" / "libsrtp" + self.assertTrue(dep_root.is_dir()) + self.assertTrue((dep_root / "include" / "srtp.h").is_file()) + self.assertIn('source_set("libsrtp")', + (dep_root / "BUILD.gn").read_text()) + + def test_argparse_rejects_unknown_dep(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + argv = [ + "generate_external_third_party.py", + "--source-root", + tempdir, + "--dep", + "does-not-exist", + "--include-root", + "/test/include", + "--lib-root", + "/test/lib", + ] + with mock.patch("sys.argv", argv): + with self.assertRaises(SystemExit): + MODULE.main() + + +if __name__ == "__main__": + unittest.main() diff --git a/ports/webrtc/package-remove-paths.txt b/ports/webrtc/package-remove-paths.txt new file mode 100644 index 00000000000000..fc60f6d8686ce4 --- /dev/null +++ b/ports/webrtc/package-remove-paths.txt @@ -0,0 +1,46 @@ +include/api/audio/test +include/api/audio_codecs/test +include/api/g3doc +include/api/metronome/test +include/api/task_queue/test +include/api/test +include/api/test/metrics/proto +include/api/transport/test +include/api/video/test +include/api/video_codecs/test +include/api/voip/test +include/common_video/test +include/logging/g3doc +include/modules/audio_coding/codecs/g711/test +include/modules/audio_coding/codecs/g722/test +include/modules/audio_coding/codecs/isac/main +include/modules/audio_coding/codecs/opus/test +include/modules/audio_coding/g3doc +include/modules/audio_coding/neteq/g3doc +include/modules/audio_coding/neteq/test +include/modules/audio_coding/neteq/test/delay_tool +include/modules/audio_coding/test +include/modules/audio_device/g3doc +include/modules/audio_mixer/g3doc +include/modules/audio_processing/g3doc +include/modules/audio_processing/test +include/modules/congestion_controller/goog_cc/test +include/modules/congestion_controller/scream/test +include/modules/desktop_capture/linux/wayland/test +include/modules/desktop_capture/win/cursor_test_data +include/modules/pacing/g3doc +include/modules/rtp_rtcp/g3doc +include/modules/rtp_rtcp/source +include/modules/rtp_rtcp/test +include/modules/utility/source +include/modules/video_capture/test +include/modules/video_coding/codecs/h264/test +include/modules/video_coding/codecs/test +include/modules/video_coding/codecs/test/batch +include/modules/video_coding/codecs/vp8/test +include/modules/video_coding/codecs/vp9/test +include/modules/video_coding/g3doc +include/p2p/g3doc +include/p2p/test +include/rtc_base/java +include/system_wrappers/source diff --git a/ports/webrtc/portfile.cmake b/ports/webrtc/portfile.cmake new file mode 100644 index 00000000000000..7231789829e7ab --- /dev/null +++ b/ports/webrtc/portfile.cmake @@ -0,0 +1,615 @@ +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +set(WEBRTC_TARGET_IS_LINUX FALSE) +set(WEBRTC_TARGET_IS_MACOS FALSE) +set(WEBRTC_TARGET_IS_WINDOWS FALSE) +if(VCPKG_TARGET_IS_LINUX AND VCPKG_TARGET_ARCHITECTURE MATCHES "^(arm64|x64)$") + set(WEBRTC_TARGET_IS_LINUX TRUE) +elseif(VCPKG_TARGET_IS_OSX AND VCPKG_TARGET_ARCHITECTURE MATCHES "^(arm64|x64)$") + set(WEBRTC_TARGET_IS_MACOS TRUE) +elseif(VCPKG_TARGET_IS_WINDOWS AND VCPKG_TARGET_ARCHITECTURE MATCHES "^(arm64|x86|x64)$") + set(WEBRTC_TARGET_IS_WINDOWS TRUE) +else() + message(FATAL_ERROR "webrtc currently supports only x64-linux, arm64-linux, arm64-osx, x64-osx, arm64-windows, x86-windows, and x64-windows.") +endif() + +set(WEBRTC_PATCHES + webrtc-0001-disable-perfetto-when-off.patch + webrtc-0002-export-enable-media-with-defaults.patch + webrtc-0005-use-external-openssl.patch + webrtc-0006-make-dav1d-decoder-deps-conditional.patch + webrtc-0007-fix-rtp-packet-info-eq-for-msvc.patch + webrtc-0008-fix-audio-device-core-win-goto-scope.patch + webrtc-0009-fix-avx2-intrinsics-for-msvc.patch + webrtc-0010-fix-denormal-disabler-for-msvc.patch + webrtc-0011-make-linux-audio-backends-optional.patch +) + +set(BUILD_PATCHES + build-0001-drop-module-deps-from-toolchain-invocations.patch + build-0002-fix-apple-arflags-usage.patch + build-0004-disable-sanitize-c-array-bounds.patch + build-0005-disable-sanitize-return.patch + build-0006-skip-local-vs-debugger-copy.patch + build-0007-fix-windows-pdb-commands.patch + build-0008-disable-crel-on-linux-arm64.patch +) + +set(WEBRTC_SOURCE_URL "https://webrtc.googlesource.com/src") +set(WEBRTC_SOURCE_REF "aa217206b9ce8b929dc56d112d670a5931ef8cc1") + +include("${CMAKE_CURRENT_LIST_DIR}/webrtc-functions.cmake") + +set(WEBRTC_CHROMIUM_THIRD_PARTY_REF "7048751cc7450f6e937418379d2a95551973625f") + +declare_webrtc_repo(build + DESTINATION "build" + URL "https://chromium.googlesource.com/chromium/src/build" + REF "f123ee3617656ae843bd7f68f173c651fe2ec4bf" + PATCHES_VAR BUILD_PATCHES +) +declare_webrtc_repo(buildtools + DESTINATION "buildtools" + URL "https://chromium.googlesource.com/chromium/src/buildtools" + REF "95ed44cf5f06dbb5861030b91c9db9ccb4316762" +) + +declare_webrtc_generated_external(third_party_root PHASE pre_absl) +declare_webrtc_generated_external(testing PHASE pre_absl) +declare_webrtc_generated_external(tools PHASE pre_absl) +declare_webrtc_generated_external(libsrtp LIB_ROOT_VAR LIBSRTP_LIB_ROOT) +declare_webrtc_generated_external(libyuv LIB_ROOT_VAR LIBYUV_LIB_ROOT) +declare_webrtc_generated_external(libvpx LIB_ROOT_VAR LIBVPX_LIB_ROOT) +declare_webrtc_generated_external(opus LIB_ROOT_VAR OPUS_LIB_ROOT) +declare_webrtc_generated_external(libaom LIB_ROOT_VAR LIBAOM_LIB_ROOT) +declare_webrtc_generated_external(jsoncpp LIB_ROOT_VAR JSONCPP_LIB_ROOT) +declare_webrtc_generated_external(pffft LIB_ROOT_VAR PFFFT_LIB_ROOT) +declare_webrtc_generated_external(alsa) +declare_webrtc_generated_external(pulseaudio) +declare_webrtc_generated_external(dav1d) +declare_webrtc_generated_external(llvm-libc) +declare_webrtc_generated_external(protobuf) +declare_webrtc_generated_external(googletest) +declare_webrtc_generated_external(catapult) +declare_webrtc_generated_external(nasm TOOL_PATH_VAR WEBRTC_NASM_PROGRAM) + +vcpkg_from_git( + OUT_SOURCE_PATH SOURCE_PATH + URL "${WEBRTC_SOURCE_URL}" + REF "${WEBRTC_SOURCE_REF}" + PATCHES ${WEBRTC_PATCHES} +) + +fetch_declared_webrtc_repos("${SOURCE_PATH}") + +vcpkg_find_acquire_program(PYTHON3) + +function(materialize_webrtc_gitiles_text_blob relative_path filename sha512) + vcpkg_download_distfile( + encoded_blob + URLS "https://chromium.googlesource.com/chromium/src/third_party/+/${WEBRTC_CHROMIUM_THIRD_PARTY_REF}/rnnoise/${relative_path}?format=TEXT" + FILENAME "webrtc-${filename}.b64" + SHA512 "${sha512}" + ) + + set(output_path "${SOURCE_PATH}/third_party/rnnoise/${relative_path}") + get_filename_component(output_dir "${output_path}" DIRECTORY) + file(MAKE_DIRECTORY "${output_dir}") + + vcpkg_execute_required_process( + COMMAND + "${PYTHON3}" "-c" + "import base64, pathlib, sys; pathlib.Path(sys.argv[2]).write_bytes(base64.b64decode(pathlib.Path(sys.argv[1]).read_bytes()))" + "${encoded_blob}" "${output_path}" + WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}" + LOGNAME "decode-${TARGET_TRIPLET}-${filename}" + ) +endfunction() + +if(WEBRTC_TARGET_IS_WINDOWS AND VCPKG_CRT_LINKAGE STREQUAL "dynamic") + vcpkg_replace_string( + "${SOURCE_PATH}/build/config/win/BUILD.gn" + " # Desktop Windows: static CRT.\n configs = [ \":static_crt\" ]\n" + " # Vcpkg Windows triplets with a dynamic CRT expect this for consumers.\n configs = [ \":dynamic_crt\" ]\n" + ) +endif() + +file(REMOVE_RECURSE "${SOURCE_PATH}/testing" "${SOURCE_PATH}/tools") + +file(REMOVE_RECURSE "${SOURCE_PATH}/third_party/rnnoise") +materialize_webrtc_gitiles_text_blob("BUILD.gn" "chromium-third_party-rnnoise-BUILD.gn" "0aed621ae4a861008771ada20909389da61d0ae767ff414374acc4c69edb23b9a391c0f2475186ad478631697e6b2aec91a4d9cbdbe4cfe03ecfb8ba9e3c39e3") +materialize_webrtc_gitiles_text_blob("COPYING" "chromium-third_party-rnnoise-COPYING" "6f1e6932c4fb632fa2fb214a6e99c9ac2982a6b4da6b1f0bf4e7154f330af0ce27851ce2e01f6f29c6ef8a5c3888c359e3bb711611526f40c2b8cca5ad104e21") +materialize_webrtc_gitiles_text_blob("DIR_METADATA" "chromium-third_party-rnnoise-DIR_METADATA" "8a53f9899d1440b380b1adffd611c94252e0e00bf8e510c8d8434348257d5aa35222fd0d6fb1b86da43515d3b06f9c0dedc472a17fd9c0eaae4af3df53c67df5") +materialize_webrtc_gitiles_text_blob("OWNERS" "chromium-third_party-rnnoise-OWNERS" "9e3179c14e2eecaf6b801dfd1208b6c3dacf2ee7ca97c0ce9e8704c30353ad4045cbea8b030a60cd40e0f13f4ca2d1d9ead14fc4b7de15857412f6797a8a9ccc") +materialize_webrtc_gitiles_text_blob("README.chromium" "chromium-third_party-rnnoise-README.chromium" "bb6c0ec6a36203e4caa573640e90767d57351ddf579fb3074aa43669b8e153c331f8cda91508435636c23dc6094f5ca6754e4fb48d6de571a1cbbb7a57850a1e") +materialize_webrtc_gitiles_text_blob("src/rnn_activations.h" "chromium-third_party-rnnoise-src-rnn_activations.h" "437d28b756ef771e32b7e1bdc52e148ea731e54f7627c3b4a82c0149666ef9bebebc6d214ef15d75c19c915eb4aa0a1b8b37bf6b782e488216fa9a862c1ce8a3") +materialize_webrtc_gitiles_text_blob("src/rnn_vad_weights.cc" "chromium-third_party-rnnoise-src-rnn_vad_weights.cc" "b0d863316a0e652d9ee0078c6316bbd2db8d311242d2fb3569b4d8fd56f6c999d65cfa9f47499c368ead8aa7f572824d01837ff4153845bc8d27f334a9c1b2e6") +materialize_webrtc_gitiles_text_blob("src/rnn_vad_weights.h" "chromium-third_party-rnnoise-src-rnn_vad_weights.h" "1e457ae5918407c0ce689d377f4a9664bc60b8db55798ede871238c69399f8aedf16442b3452adaaa7621fa0aa3bace81dbf8344df8a266813406e66c191e856") + +vcpkg_replace_string("${SOURCE_PATH}/.gn" "script_executable = \"python3\"" "script_executable = \"${PYTHON3}\"") +file(WRITE "${SOURCE_PATH}/build/config/gclient_args.gni" "# Generated for local vcpkg webrtc port\ngenerate_location_tags = true\n") +if(WEBRTC_TARGET_IS_WINDOWS OR WEBRTC_TARGET_IS_LINUX) + set(ENV{DEPOT_TOOLS_WIN_TOOLCHAIN} 0) + if(WEBRTC_TARGET_IS_WINDOWS) + file(MAKE_DIRECTORY "${SOURCE_PATH}/build/util") + file(WRITE "${SOURCE_PATH}/build/util/LASTCHANGE.committime" "0\n") + endif() + file(MAKE_DIRECTORY "${SOURCE_PATH}/third_party/compiler-rt") + file(WRITE "${SOURCE_PATH}/third_party/compiler-rt/BUILD.gn" "group(\"atomic\") {}\n") +endif() +set(WEBRTC_ABSL_EXPORTS "${CURRENT_BUILDTREES_DIR}/webrtc-absl-targets.cmake") + +function(generate_external_dep source_path dep_name include_root lib_root build_config) + set(options) + set(oneValueArgs TOOL_PATH) + cmake_parse_arguments(PARSE_ARGV 5 ARG "${options}" "${oneValueArgs}" "") + set(GENERATE_EXTERNAL_DEP_COMMAND + "${PYTHON3}" "${CMAKE_CURRENT_LIST_DIR}/generate_external_third_party.py" + --source-root "${source_path}" + --dep "${dep_name}" + --include-root "${include_root}" + --lib-root "${lib_root}" + ) + if(DEFINED ARG_TOOL_PATH AND NOT ARG_TOOL_PATH STREQUAL "") + list(APPEND GENERATE_EXTERNAL_DEP_COMMAND --tool-path "${ARG_TOOL_PATH}") + endif() + vcpkg_execute_required_process( + COMMAND ${GENERATE_EXTERNAL_DEP_COMMAND} + WORKING_DIRECTORY "${source_path}" + LOGNAME "generate-${TARGET_TRIPLET}-external-${dep_name}-${build_config}" + ) +endfunction() + +function(set_webrtc_build_config build_config) + if("${build_config}" STREQUAL "debug") + set(BUILD_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg" PARENT_SCOPE) + set(BUILD_DESTINATION "${CURRENT_PACKAGES_DIR}/debug/lib" PARENT_SCOPE) + set(IS_DEBUG "true" PARENT_SCOPE) + set(SYMBOL_LEVEL "2" PARENT_SCOPE) + set(ABSL_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + set(LIBYUV_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + set(SSL_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + set(LIBVPX_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + set(OPUS_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + set(LIBSRTP_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + set(LIBAOM_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + set(JSONCPP_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + set(PFFFT_LIB_ROOT "${CURRENT_INSTALLED_DIR}/debug/lib" PARENT_SCOPE) + else() + set(BUILD_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel" PARENT_SCOPE) + set(BUILD_DESTINATION "${CURRENT_PACKAGES_DIR}/lib" PARENT_SCOPE) + set(IS_DEBUG "false" PARENT_SCOPE) + set(SYMBOL_LEVEL "0" PARENT_SCOPE) + set(ABSL_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + set(LIBYUV_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + set(SSL_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + set(LIBVPX_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + set(OPUS_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + set(LIBSRTP_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + set(LIBAOM_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + set(JSONCPP_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + set(PFFFT_LIB_ROOT "${CURRENT_INSTALLED_DIR}/lib" PARENT_SCOPE) + endif() +endfunction() + +function(setup_webrtc_windows_toolchain build_config) + vcpkg_cmake_get_vars(cmake_vars_file) + include("${cmake_vars_file}") + + if("${build_config}" STREQUAL "debug") + set(WEBRTC_EXTRA_CFLAGS_C "${VCPKG_COMBINED_C_FLAGS_DEBUG}" PARENT_SCOPE) + set(WEBRTC_EXTRA_CFLAGS_CC "${VCPKG_COMBINED_CXX_FLAGS_DEBUG}" PARENT_SCOPE) + set(WEBRTC_EXTRA_LDFLAGS "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG}" PARENT_SCOPE) + set(WEBRTC_EXTRA_ARFLAGS "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_DEBUG}" PARENT_SCOPE) + else() + set(WEBRTC_EXTRA_CFLAGS_C "${VCPKG_COMBINED_C_FLAGS_RELEASE}" PARENT_SCOPE) + set(WEBRTC_EXTRA_CFLAGS_CC "${VCPKG_COMBINED_CXX_FLAGS_RELEASE}" PARENT_SCOPE) + set(WEBRTC_EXTRA_LDFLAGS "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE}" PARENT_SCOPE) + set(WEBRTC_EXTRA_ARFLAGS "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_RELEASE}" PARENT_SCOPE) + endif() +endfunction() + +function(setup_webrtc_host_xcode_toolchain) + find_program(XCRUN NAMES xcrun REQUIRED) + foreach(XCODE_TOOL IN ITEMS clang clang++ ar nm strip install_name_tool libtool) + execute_process( + COMMAND "${XCRUN}" --find "${XCODE_TOOL}" + OUTPUT_VARIABLE WEBRTC_${XCODE_TOOL}_PROGRAM + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE WEBRTC_${XCODE_TOOL}_RESULT + ) + if(NOT WEBRTC_${XCODE_TOOL}_RESULT EQUAL 0 OR WEBRTC_${XCODE_TOOL}_PROGRAM STREQUAL "") + message(FATAL_ERROR "Failed to locate '${XCODE_TOOL}' via xcrun.") + endif() + endforeach() + + execute_process( + COMMAND "${WEBRTC_clang_PROGRAM}" -print-resource-dir + OUTPUT_VARIABLE WEBRTC_CLANG_RESOURCE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE WEBRTC_CLANG_RESOURCE_RESULT + ) + if(NOT WEBRTC_CLANG_RESOURCE_RESULT EQUAL 0 OR WEBRTC_CLANG_RESOURCE_DIR STREQUAL "") + message(FATAL_ERROR "Failed to query clang resource dir from '${WEBRTC_clang_PROGRAM}'.") + endif() + + get_filename_component(WEBRTC_CLANG_VERSION "${WEBRTC_CLANG_RESOURCE_DIR}" NAME) + if(WEBRTC_CLANG_VERSION STREQUAL "") + message(FATAL_ERROR "Failed to derive clang version from resource dir '${WEBRTC_CLANG_RESOURCE_DIR}'.") + endif() + + execute_process( + COMMAND "${XCRUN}" --show-sdk-path + OUTPUT_VARIABLE WEBRTC_MAC_SDK_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE WEBRTC_MAC_SDK_RESULT + ) + if(NOT WEBRTC_MAC_SDK_RESULT EQUAL 0 OR WEBRTC_MAC_SDK_PATH STREQUAL "") + message(FATAL_ERROR "Failed to locate macOS SDK via xcrun.") + endif() + + set(XCODE_HOST_ROOT "${CURRENT_BUILDTREES_DIR}/xcode-host" PARENT_SCOPE) + set(XCODE_HOST_BIN_DIR "${CURRENT_BUILDTREES_DIR}/xcode-host/bin" PARENT_SCOPE) + set(XCODE_HOST_LIB_CLANG_DIR "${CURRENT_BUILDTREES_DIR}/xcode-host/lib/clang" PARENT_SCOPE) + set(HOST_CLANG_RESOURCE_DIR "${WEBRTC_CLANG_RESOURCE_DIR}" PARENT_SCOPE) + foreach(XCODE_TOOL IN ITEMS clang clang++ ar nm strip install_name_tool libtool) + set(WEBRTC_${XCODE_TOOL}_PROGRAM "${WEBRTC_${XCODE_TOOL}_PROGRAM}" PARENT_SCOPE) + endforeach() + set(WEBRTC_CLANG_BASE_PATH "${CURRENT_BUILDTREES_DIR}/xcode-host" PARENT_SCOPE) + set(WEBRTC_CLANG_VERSION "${WEBRTC_CLANG_VERSION}" PARENT_SCOPE) + set(WEBRTC_MAC_SDK_PATH "${WEBRTC_MAC_SDK_PATH}" PARENT_SCOPE) +endfunction() + +function(probe_webrtc_compiler_flag out_var compiler_path flag) + set(options) + set(oneValueArgs COMPILER_TARGET) + cmake_parse_arguments(PARSE_ARGV 3 ARG "${options}" "${oneValueArgs}" "") + + set(test_source "${CURRENT_BUILDTREES_DIR}/webrtc-flag-probe.cc") + set(test_object "${CURRENT_BUILDTREES_DIR}/webrtc-flag-probe.o") + file(WRITE "${test_source}" "int main() { return 0; }\n") + + set(probe_command "${compiler_path}") + if(DEFINED ARG_COMPILER_TARGET AND NOT ARG_COMPILER_TARGET STREQUAL "") + list(APPEND probe_command "--target=${ARG_COMPILER_TARGET}") + endif() + list(APPEND probe_command "${flag}" -c "${test_source}" -o "${test_object}") + + execute_process( + COMMAND ${probe_command} + RESULT_VARIABLE probe_result + OUTPUT_QUIET + ERROR_QUIET + ) + + file(REMOVE "${test_source}" "${test_object}") + if(probe_result EQUAL 0) + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +function(adjust_webrtc_lifetime_dse_flag source_path) + if(WEBRTC_TARGET_IS_WINDOWS) + return() + endif() + + set(compiler_target "") + if(WEBRTC_TARGET_IS_MACOS) + set(cxx_compiler "${WEBRTC_clang++_PROGRAM}") + elseif(WEBRTC_TARGET_IS_LINUX) + vcpkg_cmake_get_vars(cmake_vars_file) + include("${cmake_vars_file}") + set(cxx_compiler "${VCPKG_DETECTED_CMAKE_CXX_COMPILER}") + if(DEFINED VCPKG_DETECTED_CMAKE_CXX_COMPILER_TARGET) + set(compiler_target "${VCPKG_DETECTED_CMAKE_CXX_COMPILER_TARGET}") + endif() + else() + return() + endif() + + # Chromium added -fno-lifetime-dse in February 2026 after Android test + # failures following a Clang roll, then kept it because the benefits did + # not appear to outweigh the risks: + # https://chromium.googlesource.com/chromium/src/build/config/+/729af572f0a301b757f1040b6aad6773cd928bd3 + # https://chromium.googlesource.com/chromium/src/build/config/+/94c49aa3b3d8aadcd37d195fd0c0069829b856eb + # Keep upstream behavior when the target compiler accepts the flag, but + # strip the block for unsupported non-Windows compilers. + probe_webrtc_compiler_flag( + compiler_supports_fno_lifetime_dse + "${cxx_compiler}" + "-fno-lifetime-dse" + COMPILER_TARGET "${compiler_target}" + ) + if(compiler_supports_fno_lifetime_dse) + return() + endif() + + set(lifetime_dse_block [=[ # The performance improvement does not seem worth the risk. See + # https://crbug.com/484082200 for background and https://crrev.com/c/7593035 + # for discussion. + if (!is_wasm) { + cflags += [ "-fno-lifetime-dse" ] + } + +]=]) + + set(compiler_build_gn "${source_path}/build/config/compiler/BUILD.gn") + file(READ "${compiler_build_gn}" compiler_build_gn_contents) + string(FIND "${compiler_build_gn_contents}" "${lifetime_dse_block}" lifetime_dse_pos) + if(lifetime_dse_pos EQUAL -1) + message(FATAL_ERROR "Expected upstream -fno-lifetime-dse block was not found in '${compiler_build_gn}'.") + endif() + + string(REPLACE "${lifetime_dse_block}" "" compiler_build_gn_contents "${compiler_build_gn_contents}") + file(WRITE "${compiler_build_gn}" "${compiler_build_gn_contents}") +endfunction() + +set(GN "${CURRENT_HOST_INSTALLED_DIR}/tools/gn/gn${VCPKG_HOST_EXECUTABLE_SUFFIX}") +set(NINJA "${CURRENT_HOST_INSTALLED_DIR}/tools/ninja/ninja${VCPKG_HOST_EXECUTABLE_SUFFIX}") +if(WEBRTC_TARGET_IS_MACOS) + setup_webrtc_host_xcode_toolchain() + file(MAKE_DIRECTORY "${XCODE_HOST_BIN_DIR}" "${XCODE_HOST_LIB_CLANG_DIR}") + file(REMOVE + "${XCODE_HOST_BIN_DIR}/clang" + "${XCODE_HOST_BIN_DIR}/clang++" + "${XCODE_HOST_BIN_DIR}/llvm-ar" + "${XCODE_HOST_BIN_DIR}/llvm-nm" + "${XCODE_HOST_BIN_DIR}/llvm-strip" + "${XCODE_HOST_BIN_DIR}/llvm-install-name-tool" + "${XCODE_HOST_BIN_DIR}/libtool" + ) + file(CREATE_LINK "${WEBRTC_clang_PROGRAM}" "${XCODE_HOST_BIN_DIR}/clang" SYMBOLIC) + file(CREATE_LINK "${WEBRTC_clang++_PROGRAM}" "${XCODE_HOST_BIN_DIR}/clang++" SYMBOLIC) + string(CONFIGURE [=[ +#!/bin/bash +exec "@PYTHON3@" - "$@" <<'PY' +import pathlib +import shlex +import subprocess +import sys + +args = [] +for arg in sys.argv[1:]: + if arg.startswith("@"): + rsp_path = pathlib.Path(arg[1:]) + rsp_text = rsp_path.read_text() + args.extend(shlex.split(rsp_text)) + else: + args.append(arg) + +sys.exit(subprocess.call([r"@WEBRTC_ar_PROGRAM@", *args])) +PY +]=] WEBRTC_LLVM_AR_WRAPPER @ONLY) + file(WRITE "${XCODE_HOST_BIN_DIR}/llvm-ar" "${WEBRTC_LLVM_AR_WRAPPER}") + file(CHMOD "${XCODE_HOST_BIN_DIR}/llvm-ar" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + file(CREATE_LINK "${WEBRTC_nm_PROGRAM}" "${XCODE_HOST_BIN_DIR}/llvm-nm" SYMBOLIC) + file(CREATE_LINK "${WEBRTC_strip_PROGRAM}" "${XCODE_HOST_BIN_DIR}/llvm-strip" SYMBOLIC) + file(CREATE_LINK "${WEBRTC_install_name_tool_PROGRAM}" "${XCODE_HOST_BIN_DIR}/llvm-install-name-tool" SYMBOLIC) + file(CREATE_LINK "${WEBRTC_libtool_PROGRAM}" "${XCODE_HOST_BIN_DIR}/libtool" SYMBOLIC) + file(CREATE_LINK "${HOST_CLANG_RESOURCE_DIR}" "${XCODE_HOST_LIB_CLANG_DIR}/${WEBRTC_CLANG_VERSION}" SYMBOLIC) +endif() + +vcpkg_find_acquire_program(NASM) +set(WEBRTC_NASM_PROGRAM "${NASM}") + +adjust_webrtc_lifetime_dse_flag("${SOURCE_PATH}") + +if(NOT EXISTS "${GN}") + message(FATAL_ERROR "Missing bundled GN binary: ${GN}") +endif() + +if(NOT EXISTS "${NINJA}") + message(FATAL_ERROR "Missing bundled Ninja binary: ${NINJA}") +endif() + +foreach(BUILD_CONFIG IN ITEMS debug release) + set_webrtc_build_config("${BUILD_CONFIG}") + if(WEBRTC_TARGET_IS_WINDOWS) + setup_webrtc_windows_toolchain("${BUILD_CONFIG}") + endif() + + generate_declared_webrtc_externals("${SOURCE_PATH}" "${BUILD_CONFIG}" pre_absl) + file(REMOVE_RECURSE "${SOURCE_PATH}/third_party/abseil-cpp") + vcpkg_execute_required_process( + COMMAND + "${PYTHON3}" "${CMAKE_CURRENT_LIST_DIR}/generate_external_absl.py" + --manifest "${CMAKE_CURRENT_LIST_DIR}/absl-labels.txt" + --output "${SOURCE_PATH}/third_party/abseil-cpp" + --include-root "${CURRENT_INSTALLED_DIR}/include" + --lib-root "${ABSL_LIB_ROOT}" + --cmake-absl-targets "${CURRENT_INSTALLED_DIR}/share/absl/abslTargets.cmake" + --cmake-output "${WEBRTC_ABSL_EXPORTS}" + WORKING_DIRECTORY "${SOURCE_PATH}" + LOGNAME "generate-${TARGET_TRIPLET}-external-absl-${BUILD_CONFIG}" + ) + generate_declared_webrtc_externals("${SOURCE_PATH}" "${BUILD_CONFIG}" post_absl) + + set(WEBRTC_GN_ARGS + "clang_use_chrome_plugins=false" + "init_stack_vars=false" + "use_sysroot=false" + "use_custom_libcxx=false" + "use_custom_libcxx_for_host=false" + "use_clang_modules=false" + "use_siso=false" + "use_remoteexec=false" + "is_debug=${IS_DEBUG}" + "is_component_build=false" + "symbol_level=${SYMBOL_LEVEL}" + "treat_warnings_as_errors=false" + "rtc_build_examples=false" + "rtc_build_tools=false" + "rtc_include_tests=false" + "rtc_enable_protobuf=false" + "rtc_build_ssl=false" + "rtc_ssl_root=\"${CURRENT_INSTALLED_DIR}/include\"" + "rtc_ssl_lib_path=\"${SSL_LIB_ROOT}\"" + "libsrtp_build_boringssl=false" + "libsrtp_ssl_root=\"${CURRENT_INSTALLED_DIR}/include\"" + "use_system_libjpeg=true" + "use_libjpeg_turbo=false" + "rtc_build_libvpx=true" + "rtc_build_opus=true" + "rtc_include_dav1d_in_internal_decoder_factory=false" + "rtc_use_pipewire=false" + "rtc_use_x11=false" + "use_glib=false" + "enable_rust=false" + "enable_rust_cxx=false" + "rtc_use_h264=false" + ) + if(WEBRTC_TARGET_IS_LINUX) + if("alsa" IN_LIST FEATURES) + list(APPEND WEBRTC_GN_ARGS "rtc_include_alsa_audio=true") + else() + list(APPEND WEBRTC_GN_ARGS "rtc_include_alsa_audio=false") + endif() + if("pulseaudio" IN_LIST FEATURES) + list(APPEND WEBRTC_GN_ARGS "rtc_include_pulse_audio=true") + else() + list(APPEND WEBRTC_GN_ARGS "rtc_include_pulse_audio=false") + endif() + list(APPEND WEBRTC_GN_ARGS + "target_os=\"linux\"" + "target_cpu=\"${VCPKG_TARGET_ARCHITECTURE}\"" + "custom_toolchain=\"//build/toolchain/linux:${VCPKG_TARGET_ARCHITECTURE}\"" + "is_clang=false" + "use_lld=false" + ) + elseif(WEBRTC_TARGET_IS_MACOS) + list(APPEND WEBRTC_GN_ARGS + "target_os=\"mac\"" + "target_cpu=\"${VCPKG_TARGET_ARCHITECTURE}\"" + "custom_toolchain=\"//build/toolchain/mac:clang_${VCPKG_TARGET_ARCHITECTURE}\"" + "host_toolchain=\"//build/toolchain/mac:clang_${VCPKG_TARGET_ARCHITECTURE}\"" + "is_clang=true" + "clang_base_path=\"${WEBRTC_CLANG_BASE_PATH}\"" + "clang_version=\"${WEBRTC_CLANG_VERSION}\"" + "mac_sdk_path=\"${WEBRTC_MAC_SDK_PATH}\"" + ) + elseif(WEBRTC_TARGET_IS_WINDOWS) + list(APPEND WEBRTC_GN_ARGS + "target_os=\"win\"" + "target_cpu=\"${VCPKG_TARGET_ARCHITECTURE}\"" + "is_clang=false" + "use_lld=false" + "extra_cflags_c=\"${WEBRTC_EXTRA_CFLAGS_C}\"" + "extra_cflags_cc=\"${WEBRTC_EXTRA_CFLAGS_CC}\"" + "extra_ldflags=\"${WEBRTC_EXTRA_LDFLAGS}\"" + "extra_arflags=\"${WEBRTC_EXTRA_ARFLAGS}\"" + ) + if("${BUILD_CONFIG}" STREQUAL "debug") + list(APPEND WEBRTC_GN_ARGS "enable_iterator_debugging=true") + endif() + endif() + string(JOIN " " GN_ARGS ${WEBRTC_GN_ARGS}) + + vcpkg_execute_required_process( + COMMAND "${GN}" gen "${BUILD_DIR}" "--args=${GN_ARGS}" + WORKING_DIRECTORY "${SOURCE_PATH}" + LOGNAME "generate-${TARGET_TRIPLET}-${BUILD_CONFIG}" + ) + + vcpkg_execute_required_process( + COMMAND "${NINJA}" -C "${BUILD_DIR}" webrtc + WORKING_DIRECTORY "${SOURCE_PATH}" + LOGNAME "build-${TARGET_TRIPLET}-${BUILD_CONFIG}" + ) + + if(WEBRTC_TARGET_IS_WINDOWS) + file(INSTALL "${BUILD_DIR}/obj/webrtc.lib" DESTINATION "${BUILD_DESTINATION}") + else() + file(INSTALL "${BUILD_DIR}/obj/libwebrtc.a" DESTINATION "${BUILD_DESTINATION}") + endif() +endforeach() + +set(WEBRTC_LINUX_INTERFACE_DEFINITIONS "USE_AURA=1;USE_OZONE=1;USE_UDEV;WEBRTC_LINUX;WEBRTC_POSIX") +if(VCPKG_TARGET_IS_LINUX AND VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + string(PREPEND WEBRTC_LINUX_INTERFACE_DEFINITIONS "LIBYUV_DISABLE_NEON;WEBRTC_ENABLE_AVX2;") +endif() + +set(WEBRTC_COMMON_INTERFACE_COMPILE_DEFINITIONS + CHROMIUM + DYNAMIC_ANNOTATIONS_ENABLED=1 + LIBYUV_DISABLE_SVE + LIBYUV_DISABLE_SME + LIBYUV_DISABLE_LSX + LIBYUV_DISABLE_LASX + PROTOBUF_ENABLE_DEBUG_LOGGING_MAY_LEAK_PII=0 + RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY + RTC_ENABLE_VP9 + WEBRTC_DEPRECATE_PLAN_B + WEBRTC_ENCODER_PSNR_STATS + WEBRTC_HAVE_SCTP + WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE + WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=0 + WEBRTC_STRICT_FIELD_TRIALS=0 +) +set(WEBRTC_INTERFACE_COMPILE_DEFINITIONS ${WEBRTC_COMMON_INTERFACE_COMPILE_DEFINITIONS}) +set(WEBRTC_INTERFACE_LINK_OPTIONS) + +if(VCPKG_TARGET_IS_LINUX) + list(APPEND WEBRTC_INTERFACE_COMPILE_DEFINITIONS ${WEBRTC_LINUX_INTERFACE_DEFINITIONS}) +elseif(VCPKG_TARGET_IS_OSX) + list(APPEND WEBRTC_INTERFACE_COMPILE_DEFINITIONS + WEBRTC_MAC + WEBRTC_POSIX + ) + list(APPEND WEBRTC_INTERFACE_LINK_OPTIONS + "SHELL:-framework AudioToolbox" + "SHELL:-framework CoreAudio" + "SHELL:-framework CoreFoundation" + "SHELL:-framework Foundation" + "SHELL:-framework AppKit" + "SHELL:-framework ApplicationServices" + ) +elseif(VCPKG_TARGET_IS_WINDOWS) + list(APPEND WEBRTC_INTERFACE_COMPILE_DEFINITIONS + NOMINMAX + UNICODE + WIN32 + WIN32_LEAN_AND_MEAN + WEBRTC_WIN + ) +endif() + +foreach(HEADER_ROOT IN ITEMS + api + common_video + logging + media + modules + p2p + rtc_base + system_wrappers +) + file(INSTALL + DIRECTORY "${SOURCE_PATH}/${HEADER_ROOT}/" + DESTINATION "${CURRENT_PACKAGES_DIR}/include/${HEADER_ROOT}" + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.hpp" + PATTERN "*.inc" + ) +endforeach() + +file(STRINGS "${CMAKE_CURRENT_LIST_DIR}/package-remove-paths.txt" PACKAGE_REMOVE_PATHS ENCODING UTF-8) +foreach(REMOVE_PATH IN LISTS PACKAGE_REMOVE_PATHS) + if(REMOVE_PATH MATCHES "^[ \t]*#" OR REMOVE_PATH STREQUAL "") + continue() + endif() + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/${REMOVE_PATH}") +endforeach() + +file(REMOVE "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg_abi_info.txt") + +configure_file( + "${CMAKE_CURRENT_LIST_DIR}/unofficial-webrtcConfig.cmake.in" + "${CURRENT_PACKAGES_DIR}/share/unofficial-webrtc/unofficial-webrtcConfig.cmake" + @ONLY +) +file(INSTALL "${WEBRTC_ABSL_EXPORTS}" DESTINATION "${CURRENT_PACKAGES_DIR}/share/unofficial-webrtc") + +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") diff --git a/ports/webrtc/unofficial-webrtcConfig.cmake.in b/ports/webrtc/unofficial-webrtcConfig.cmake.in new file mode 100644 index 00000000000000..44f2fb23c9d9ac --- /dev/null +++ b/ports/webrtc/unofficial-webrtcConfig.cmake.in @@ -0,0 +1,64 @@ +include(CMakeFindDependencyMacro) + +find_dependency(absl CONFIG) +find_dependency(AOM CONFIG) +find_dependency(libyuv CONFIG) +find_dependency(unofficial-libvpx CONFIG) +find_dependency(Opus CONFIG) +find_dependency(OpenSSL CONFIG) +find_dependency(libSRTP CONFIG) +find_dependency(jsoncpp CONFIG) +find_dependency(pffft CONFIG) +find_dependency(Threads) + +get_filename_component(_webrtc_prefix "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) +include("${CMAKE_CURRENT_LIST_DIR}/webrtc-absl-targets.cmake") + +set(_webrtc_transitive_archives + ${WEBRTC_ABSL_STATIC_ARCHIVES} + AOM::aom + yuv + unofficial::libvpx::libvpx + Opus::opus + JsonCpp::JsonCpp + pffft::pffft + libSRTP::srtp2 + OpenSSL::SSL +) +set(_webrtc_imported_location_debug "${_webrtc_prefix}/debug/lib/libwebrtc.a") +set(_webrtc_imported_location_release "${_webrtc_prefix}/lib/libwebrtc.a") +set(_webrtc_interface_link_libraries + "Threads::Threads;${WEBRTC_ABSL_IMPORTED_TARGETS};${_webrtc_transitive_archives}" +) +set(_webrtc_windows_system_libraries + ws2_32 + winmm + iphlpapi + msdmo + dmoguids + wmcodecdspuuid +) + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(_webrtc_imported_location_debug "${_webrtc_prefix}/debug/lib/webrtc.lib") + set(_webrtc_imported_location_release "${_webrtc_prefix}/lib/webrtc.lib") + set(_webrtc_interface_link_libraries + "${WEBRTC_ABSL_IMPORTED_TARGETS};${_webrtc_transitive_archives};${_webrtc_windows_system_libraries}" + ) +endif() + +if(NOT TARGET unofficial::webrtc::webrtc) + add_library(unofficial::webrtc::webrtc STATIC IMPORTED) + set_target_properties(unofficial::webrtc::webrtc PROPERTIES + IMPORTED_CONFIGURATIONS "DEBUG;RELEASE" + IMPORTED_LOCATION "${_webrtc_imported_location_release}" + IMPORTED_LOCATION_DEBUG "${_webrtc_imported_location_debug}" + IMPORTED_LOCATION_RELEASE "${_webrtc_imported_location_release}" + MAP_IMPORTED_CONFIG_MINSIZEREL Release + MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release + INTERFACE_INCLUDE_DIRECTORIES "${_webrtc_prefix}/include" + INTERFACE_LINK_LIBRARIES "${_webrtc_interface_link_libraries}" + INTERFACE_LINK_OPTIONS "@WEBRTC_INTERFACE_LINK_OPTIONS@" + INTERFACE_COMPILE_DEFINITIONS "@WEBRTC_INTERFACE_COMPILE_DEFINITIONS@" + ) +endif() diff --git a/ports/webrtc/vcpkg.json b/ports/webrtc/vcpkg.json new file mode 100644 index 00000000000000..f6b183240df34b --- /dev/null +++ b/ports/webrtc/vcpkg.json @@ -0,0 +1,56 @@ +{ + "name": "webrtc", + "version-date": "2026-03-17", + "description": "WebRTC real-time communication library", + "homepage": "https://webrtc.googlesource.com/src", + "license": "BSD-3-Clause", + "supports": "(linux & (arm64 | x64)) | (osx & (arm64 | x64)) | (windows & (arm64 | x86 | x64))", + "dependencies": [ + "abseil", + "aom", + "jsoncpp", + { + "name": "libsrtp", + "features": [ + "openssl" + ] + }, + "libvpx", + "libyuv", + "openssl", + "opus", + "pffft", + { + "name": "vcpkg-cmake-get-vars", + "host": true + }, + { + "name": "vcpkg-tool-gn", + "host": true + }, + { + "name": "vcpkg-tool-ninja", + "host": true + } + ], + "features": { + "alsa": { + "description": "Enable ALSA Linux audio backend", + "dependencies": [ + { + "name": "alsa", + "platform": "linux" + } + ] + }, + "pulseaudio": { + "description": "Enable PulseAudio Linux audio backend", + "dependencies": [ + { + "name": "pulseaudio", + "platform": "linux" + } + ] + } + } +} diff --git a/ports/webrtc/webrtc-0001-disable-perfetto-when-off.patch b/ports/webrtc/webrtc-0001-disable-perfetto-when-off.patch new file mode 100644 index 00000000000000..5919caa7a6c78b --- /dev/null +++ b/ports/webrtc/webrtc-0001-disable-perfetto-when-off.patch @@ -0,0 +1,22 @@ +--- a/rtc_base/BUILD.gn ++++ b/rtc_base/BUILD.gn +@@ -236,18 +236,16 @@ rtc_library("logging") { + "//third_party/abseil-cpp/absl/base:nullability", + "//third_party/abseil-cpp/absl/strings:string_view", + ] +- all_dependent_configs = [ "//third_party/perfetto/gn:public_config" ] + if (rtc_use_perfetto) { ++ all_dependent_configs = [ "//third_party/perfetto/gn:public_config" ] + if (build_with_chromium) { + deps += [ "//third_party/perfetto:libperfetto" ] + } else { + deps += [ + "//third_party/perfetto/include/perfetto/tracing", + "//third_party/perfetto/src/tracing:client_api_without_backends", + "//third_party/perfetto/src/tracing:platform_impl", + ] + } +- } else { +- deps += [ "//third_party/perfetto/include/perfetto/tracing" ] + } + } diff --git a/ports/webrtc/webrtc-0002-export-enable-media-with-defaults.patch b/ports/webrtc/webrtc-0002-export-enable-media-with-defaults.patch new file mode 100644 index 00000000000000..9001789e00a9f3 --- /dev/null +++ b/ports/webrtc/webrtc-0002-export-enable-media-with-defaults.patch @@ -0,0 +1,10 @@ +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -512,6 +512,7 @@ + "api:create_modular_peer_connection_factory", + "api:create_peerconnection_factory", + "api:enable_media", ++ "api:enable_media_with_defaults", + "api:rtc_error", + "api:transport_api", + "api/audio_codecs:opus_audio_decoder_factory", diff --git a/ports/webrtc/webrtc-0005-use-external-openssl.patch b/ports/webrtc/webrtc-0005-use-external-openssl.patch new file mode 100644 index 00000000000000..b90f6e6ba61716 --- /dev/null +++ b/ports/webrtc/webrtc-0005-use-external-openssl.patch @@ -0,0 +1,28 @@ +diff --git a/BUILD.gn b/BUILD.gn +index 376fdb4baf..f4978f3258 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -484,6 +484,9 @@ if (!rtc_build_ssl) { + config("external_ssl_library") { + if (rtc_ssl_root != "") { + include_dirs = [ rtc_ssl_root ] ++ } ++ if (rtc_ssl_lib_path != "") { ++ lib_dirs = [ rtc_ssl_lib_path ] + } + libs = [ + "crypto", +diff --git a/webrtc.gni b/webrtc.gni +index 6be4fd36ff..b7343376a0 100644 +--- a/webrtc.gni ++++ b/webrtc.gni +@@ -103,6 +103,9 @@ declare_args() { + # library that comes with WebRTC (i.e. rtc_build_ssl == 0). + rtc_ssl_root = "" + ++ # Used to specify an external OpenSSL library path when rtc_build_ssl == 0. ++ rtc_ssl_lib_path = "" ++ + # Selects whether debug dumps for the audio processing module + # should be generated. + apm_debug_dump = false diff --git a/ports/webrtc/webrtc-0006-make-dav1d-decoder-deps-conditional.patch b/ports/webrtc/webrtc-0006-make-dav1d-decoder-deps-conditional.patch new file mode 100644 index 00000000000000..6465a9809bdb8e --- /dev/null +++ b/ports/webrtc/webrtc-0006-make-dav1d-decoder-deps-conditional.patch @@ -0,0 +1,46 @@ +diff --git a/BUILD.gn b/BUILD.gn +index 4d154f8e19..9878e420ff 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -520,7 +520,6 @@ if (!build_with_chromium) { + "api/task_queue", + "api/task_queue:default_task_queue_factory", + "api/video_codecs:video_decoder_factory_template", +- "api/video_codecs:video_decoder_factory_template_dav1d_adapter", + "api/video_codecs:video_decoder_factory_template_libvpx_vp8_adapter", + "api/video_codecs:video_decoder_factory_template_libvpx_vp9_adapter", + "api/video_codecs:video_decoder_factory_template_open_h264_adapter", +@@ -542,6 +541,10 @@ if (!build_with_chromium) { + "video", + ] + ++ if (rtc_include_dav1d_in_internal_decoder_factory) { ++ deps += [ "api/video_codecs:video_decoder_factory_template_dav1d_adapter" ] ++ } ++ + if (rtc_include_builtin_audio_codecs) { + deps += [ + "api/audio_codecs:builtin_audio_decoder_factory", +diff --git a/media/BUILD.gn b/media/BUILD.gn +index 9b026bf244..bfcd6690bc 100644 +--- a/media/BUILD.gn ++++ b/media/BUILD.gn +@@ -1007,7 +1007,6 @@ if (rtc_include_tests) { + "../api/video_codecs:scalability_mode", + "../api/video_codecs:video_codecs_api", + "../api/video_codecs:video_decoder_factory_template", +- "../api/video_codecs:video_decoder_factory_template_dav1d_adapter", + "../api/video_codecs:video_decoder_factory_template_libvpx_vp8_adapter", + "../api/video_codecs:video_decoder_factory_template_libvpx_vp9_adapter", + "../api/video_codecs:video_decoder_factory_template_open_h264_adapter", +@@ -1064,6 +1063,10 @@ if (rtc_include_tests) { + "//third_party/abseil-cpp/absl/strings:string_view", + ] + ++ if (rtc_include_dav1d_in_internal_decoder_factory) { ++ deps += [ "../api/video_codecs:video_decoder_factory_template_dav1d_adapter" ] ++ } ++ + if (enable_libaom) { + defines += [ "RTC_USE_LIBAOM_AV1_ENCODER" ] + } diff --git a/ports/webrtc/webrtc-0007-fix-rtp-packet-info-eq-for-msvc.patch b/ports/webrtc/webrtc-0007-fix-rtp-packet-info-eq-for-msvc.patch new file mode 100644 index 00000000000000..971a7c414c2b25 --- /dev/null +++ b/ports/webrtc/webrtc-0007-fix-rtp-packet-info-eq-for-msvc.patch @@ -0,0 +1,22 @@ +diff --git a/api/rtp_packet_info.cc b/api/rtp_packet_info.cc +index 89f9dfe..f795d55 100644 +--- a/api/rtp_packet_info.cc ++++ b/api/rtp_packet_info.cc +@@ -74,6 +74,15 @@ RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header, + + absolute_capture_time_ = extension.absolute_capture_time; + } + +-bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) = default; ++bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) { ++ return lhs.sequence_number_ == rhs.sequence_number_ && ++ lhs.ssrc_ == rhs.ssrc_ && ++ lhs.csrcs_ == rhs.csrcs_ && ++ lhs.rtp_timestamp_ == rhs.rtp_timestamp_ && ++ lhs.receive_time_ == rhs.receive_time_ && ++ lhs.audio_level_ == rhs.audio_level_ && ++ lhs.absolute_capture_time_ == rhs.absolute_capture_time_ && ++ lhs.local_capture_clock_offset_ == rhs.local_capture_clock_offset_; ++} + + } // namespace webrtc diff --git a/ports/webrtc/webrtc-0008-fix-audio-device-core-win-goto-scope.patch b/ports/webrtc/webrtc-0008-fix-audio-device-core-win-goto-scope.patch new file mode 100644 index 00000000000000..a47a6cef0f525b --- /dev/null +++ b/ports/webrtc/webrtc-0008-fix-audio-device-core-win-goto-scope.patch @@ -0,0 +1,304 @@ +diff --git a/modules/audio_device/win/audio_device_core_win.cc b/modules/audio_device/win/audio_device_core_win.cc +index fee76426cb..6e82f3d5df 100644 +--- a/modules/audio_device/win/audio_device_core_win.cc ++++ b/modules/audio_device/win/audio_device_core_win.cc +@@ -790,6 +790,7 @@ int32_t AudioDeviceWindowsCore::SpeakerVolumeIsAvailable(bool& available) { + HRESULT hr = S_OK; + IAudioSessionManager* pManager = NULL; + ISimpleAudioVolume* pVolume = NULL; ++ float volume = 0.0f; + + hr = _ptrDeviceOut->Activate(__uuidof(IAudioSessionManager), CLSCTX_ALL, NULL, + (void**)&pManager); +@@ -798,7 +799,6 @@ int32_t AudioDeviceWindowsCore::SpeakerVolumeIsAvailable(bool& available) { + hr = pManager->GetSimpleAudioVolume(NULL, FALSE, &pVolume); + EXIT_ON_ERROR(hr); + +- float volume(0.0f); + hr = pVolume->GetMasterVolume(&volume); + if (FAILED(hr)) { + available = false; +@@ -976,13 +976,13 @@ int32_t AudioDeviceWindowsCore::SetSpeakerMute(bool enable) { + + HRESULT hr = S_OK; + IAudioEndpointVolume* pVolume = NULL; ++ const BOOL mute = enable; + + // Set the speaker system mute state. + hr = _ptrDeviceOut->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, + reinterpret_cast(&pVolume)); + EXIT_ON_ERROR(hr); + +- const BOOL mute(enable); + hr = pVolume->SetMute(mute, NULL); + EXIT_ON_ERROR(hr); + +@@ -1083,13 +1083,13 @@ int32_t AudioDeviceWindowsCore::SetMicrophoneMute(bool enable) { + + HRESULT hr = S_OK; + IAudioEndpointVolume* pVolume = NULL; ++ const BOOL mute = enable; + + // Set the microphone system mute state. + hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, + reinterpret_cast(&pVolume)); + EXIT_ON_ERROR(hr); + +- const BOOL mute(enable); + hr = pVolume->SetMute(mute, NULL); + EXIT_ON_ERROR(hr); + +@@ -1231,12 +1231,12 @@ int32_t AudioDeviceWindowsCore::MicrophoneVolumeIsAvailable(bool& available) { + + HRESULT hr = S_OK; + IAudioEndpointVolume* pVolume = NULL; ++ float volume = 0.0f; + + hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, + reinterpret_cast(&pVolume)); + EXIT_ON_ERROR(hr); + +- float volume(0.0f); + hr = pVolume->GetMasterVolumeLevelScalar(&volume); + if (FAILED(hr)) { + available = false; +@@ -1823,6 +1823,9 @@ int32_t AudioDeviceWindowsCore::InitPlayout() { + WAVEFORMATEX* pWfxOut = NULL; + WAVEFORMATEX Wfx = WAVEFORMATEX(); + WAVEFORMATEX* pWfxClosestMatch = NULL; ++ UINT bufferFrameCount = 0; ++ constexpr int freqs[] = {48000, 44100, 16000, 96000, 32000, 8000}; ++ REFERENCE_TIME hnsBufferDuration = 0; + + // Create COM object with IAudioClient interface. + SAFE_RELEASE(_ptrClientOut); +@@ -1856,7 +1859,6 @@ int32_t AudioDeviceWindowsCore::InitPlayout() { + Wfx.wBitsPerSample = 16; + Wfx.cbSize = 0; + +- const int freqs[] = {48000, 44100, 16000, 96000, 32000, 8000}; + hr = S_FALSE; + + // Iterate over frequencies and channels, in order of priority +@@ -1941,8 +1943,6 @@ int32_t AudioDeviceWindowsCore::InitPlayout() { + // buffer. + // **************************************************************************** + // +- REFERENCE_TIME hnsBufferDuration = +- 0; // ask for minimum buffer size (default) + if (_devicePlaySampleRate == 44100) { + // Ask for a larger buffer size (30ms) when using 44.1kHz as render rate. + // There seems to be a larger risk of underruns for 44.1 compared +@@ -1982,7 +1982,6 @@ int32_t AudioDeviceWindowsCore::InitPlayout() { + + // Get the actual size of the shared (endpoint buffer). + // Typical value is 960 audio frames <=> 20ms @ 48kHz sample rate. +- UINT bufferFrameCount(0); + hr = _ptrClientOut->GetBufferSize(&bufferFrameCount); + if (SUCCEEDED(hr)) { + RTC_LOG(LS_VERBOSE) << "IAudioClient::GetBufferSize() => " +@@ -2138,6 +2137,8 @@ int32_t AudioDeviceWindowsCore::InitRecording() { + WAVEFORMATEX* pWfxIn = NULL; + WAVEFORMATEXTENSIBLE Wfx = WAVEFORMATEXTENSIBLE(); + WAVEFORMATEX* pWfxClosestMatch = NULL; ++ UINT bufferFrameCount = 0; ++ constexpr int freqs[6] = {48000, 44100, 16000, 96000, 32000, 8000}; + + // Create COM object with IAudioClient interface. + SAFE_RELEASE(_ptrClientIn); +@@ -2174,7 +2175,6 @@ int32_t AudioDeviceWindowsCore::InitRecording() { + Wfx.Samples.wValidBitsPerSample = Wfx.Format.wBitsPerSample; + Wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + +- const int freqs[6] = {48000, 44100, 16000, 96000, 32000, 8000}; + hr = S_FALSE; + + // Iterate over frequencies and channels, in order of priority +@@ -2271,7 +2271,6 @@ int32_t AudioDeviceWindowsCore::InitRecording() { + + // Get the actual size of the shared (endpoint buffer). + // Typical value is 960 audio frames <=> 20ms @ 48kHz sample rate. +- UINT bufferFrameCount(0); + hr = _ptrClientIn->GetBufferSize(&bufferFrameCount); + if (SUCCEEDED(hr)) { + RTC_LOG(LS_VERBOSE) << "IAudioClient::GetBufferSize() => " +@@ -2637,6 +2636,13 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() { + HANDLE waitArray[2] = {_hShutdownRenderEvent, _hRenderSamplesReadyEvent}; + HRESULT hr = S_OK; + HANDLE hMmTask = NULL; ++ IAudioClock* clock = NULL; ++ REFERENCE_TIME latency = 0; ++ REFERENCE_TIME devPeriod = 0; ++ REFERENCE_TIME devPeriodMin = 0; ++ BYTE* pData = NULL; ++ int playout_delay = 0; ++ double endpointBufferSizeMS = 0.0; + + // Initialize COM as MTA in this thread. + ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA); +@@ -2669,7 +2675,6 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() { + + _Lock(); + +- IAudioClock* clock = NULL; + + // Get size of rendering buffer (length is expressed as the number of audio + // frames the buffer can hold). This value is fixed during the rendering +@@ -2683,7 +2688,6 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() { + // Get maximum latency for the current stream (will not change for the + // lifetime of the IAudioClient object). + // +- REFERENCE_TIME latency; + _ptrClientOut->GetStreamLatency(&latency); + RTC_LOG(LS_VERBOSE) << "[REND] max stream latency : " << (DWORD)latency + << " (" << (double)(latency / 10000.0) << " ms)"; +@@ -2698,8 +2702,6 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() { + // an audio application can achieve. Typical value: 100000 <=> 0.01 sec = + // 10ms. + // +- REFERENCE_TIME devPeriod = 0; +- REFERENCE_TIME devPeriodMin = 0; + _ptrClientOut->GetDevicePeriod(&devPeriod, &devPeriodMin); + RTC_LOG(LS_VERBOSE) << "[REND] device period : " << (DWORD)devPeriod + << " (" << (double)(devPeriod / 10000.0) << " ms)"; +@@ -2707,20 +2709,19 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() { + // Derive initial rendering delay. + // Example: 10*(960/480) + 15 = 20 + 15 = 35ms + // +- int playout_delay = 10 * (bufferLength / _playBlockSize) + +- (int)((latency + devPeriod) / 10000); ++ playout_delay = 10 * (bufferLength / _playBlockSize) + ++ (int)((latency + devPeriod) / 10000); + _sndCardPlayDelay = playout_delay; + _writtenSamples = 0; + RTC_LOG(LS_VERBOSE) << "[REND] initial delay : " << playout_delay; + +- double endpointBufferSizeMS = ++ endpointBufferSizeMS = + 10.0 * ((double)bufferLength / (double)_devicePlayBlockSize); + RTC_LOG(LS_VERBOSE) << "[REND] endpointBufferSizeMS : " + << endpointBufferSizeMS; + + // Before starting the stream, fill the rendering buffer with silence. + // +- BYTE* pData = NULL; + hr = _ptrRenderClient->GetBuffer(bufferLength, &pData); + EXIT_ON_ERROR(hr); + +@@ -3071,11 +3072,18 @@ DWORD AudioDeviceWindowsCore::DoCaptureThread() { + HRESULT hr = S_OK; + + LARGE_INTEGER t1; ++ UINT32 syncBufferSize = 0; ++ REFERENCE_TIME latency = 0; ++ REFERENCE_TIME devPeriod = 0; ++ REFERENCE_TIME devPeriodMin = 0; ++ double extraDelayMS = 0.0; ++ double endpointBufferSizeMS = 0.0; + + BYTE* syncBuffer = NULL; + UINT32 syncBufIndex = 0; + + _readSamples = 0; ++ BYTE* pData = 0; + + // Initialize COM as MTA in this thread. + ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA); +@@ -3109,7 +3117,7 @@ DWORD AudioDeviceWindowsCore::DoCaptureThread() { + // It is used for compensation between native 44.1 and internal 44.0 and + // for cases when the capture buffer is larger than 10ms. + // +- const UINT32 syncBufferSize = 2 * (bufferLength * _recAudioFrameSize); ++ syncBufferSize = 2 * (bufferLength * _recAudioFrameSize); + syncBuffer = new BYTE[syncBufferSize]; + if (syncBuffer == NULL) { + return (DWORD)E_POINTER; +@@ -3120,7 +3128,6 @@ DWORD AudioDeviceWindowsCore::DoCaptureThread() { + // Get maximum latency for the current stream (will not change for the + // lifetime of the IAudioClient object). + // +- REFERENCE_TIME latency; + _ptrClientIn->GetStreamLatency(&latency); + RTC_LOG(LS_VERBOSE) << "[CAPT] max stream latency : " << (DWORD)latency + << " (" << (double)(latency / 10000.0) << " ms)"; +@@ -3128,17 +3135,14 @@ DWORD AudioDeviceWindowsCore::DoCaptureThread() { + // Get the length of the periodic interval separating successive processing + // passes by the audio engine on the data in the endpoint buffer. + // +- REFERENCE_TIME devPeriod = 0; +- REFERENCE_TIME devPeriodMin = 0; + _ptrClientIn->GetDevicePeriod(&devPeriod, &devPeriodMin); + RTC_LOG(LS_VERBOSE) << "[CAPT] device period : " << (DWORD)devPeriod + << " (" << (double)(devPeriod / 10000.0) << " ms)"; + +- double extraDelayMS = (double)((latency + devPeriod) / 10000.0); ++ extraDelayMS = (double)((latency + devPeriod) / 10000.0); + RTC_LOG(LS_VERBOSE) << "[CAPT] extraDelayMS : " << extraDelayMS; + +- double endpointBufferSizeMS = +- 10.0 * ((double)bufferLength / (double)_recBlockSize); ++ endpointBufferSizeMS = 10.0 * ((double)bufferLength / (double)_recBlockSize); + RTC_LOG(LS_VERBOSE) << "[CAPT] endpointBufferSizeMS : " + << endpointBufferSizeMS; + +@@ -3174,7 +3178,7 @@ DWORD AudioDeviceWindowsCore::DoCaptureThread() { + } + + while (keepRecording) { +- BYTE* pData = 0; ++ pData = 0; + UINT32 framesAvailable = 0; + DWORD flags = 0; + UINT64 recTime = 0; +@@ -3951,6 +3955,11 @@ int32_t AudioDeviceWindowsCore::_EnumerateEndpointDevicesAll( + IPropertyStore* pProps = NULL; + IAudioEndpointVolume* pEndpointVolume = NULL; + LPWSTR pwszID = NULL; ++ PROPVARIANT varName; ++ DWORD dwState = 0; ++ DWORD dwHwSupportMask = 0; ++ UINT count = 0; ++ UINT nChannelCount = 0; + + // Generate a collection of audio endpoint devices in the system. + // Get states for *all* endpoint devices. +@@ -3964,7 +3973,6 @@ int32_t AudioDeviceWindowsCore::_EnumerateEndpointDevicesAll( + + // use the IMMDeviceCollection interface... + +- UINT count = 0; + + // Retrieve a count of the devices in the device collection. + hr = pCollection->GetCount(&count); +@@ -4004,7 +4012,6 @@ int32_t AudioDeviceWindowsCore::_EnumerateEndpointDevicesAll( + + // use the IPropertyStore interface... + +- PROPVARIANT varName; + // Initialize container for property value. + PropVariantInit(&varName); + +@@ -4015,7 +4022,6 @@ int32_t AudioDeviceWindowsCore::_EnumerateEndpointDevicesAll( + RTC_LOG(LS_VERBOSE) << "friendly name: \"" << varName.pwszVal << "\""; + + // Get the endpoint's current device state +- DWORD dwState; + hr = pEndpoint->GetState(&dwState); + CONTINUE_ON_ERROR(hr); + if (dwState & DEVICE_STATE_ACTIVE) +@@ -4028,7 +4034,6 @@ int32_t AudioDeviceWindowsCore::_EnumerateEndpointDevicesAll( + RTC_LOG(LS_VERBOSE) << "state (0x" << ToHex(dwState) << ") : UNPLUGGED"; + + // Check the hardware volume capabilities. +- DWORD dwHwSupportMask = 0; + hr = pEndpoint->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, + (void**)&pEndpointVolume); + CONTINUE_ON_ERROR(hr); +@@ -4049,7 +4054,6 @@ int32_t AudioDeviceWindowsCore::_EnumerateEndpointDevicesAll( + + // Check the channel count (#channels in the audio stream that enters or + // leaves the audio endpoint device) +- UINT nChannelCount(0); + hr = pEndpointVolume->GetChannelCount(&nChannelCount); + CONTINUE_ON_ERROR(hr); + RTC_LOG(LS_VERBOSE) << "#channels : " << nChannelCount; diff --git a/ports/webrtc/webrtc-0009-fix-avx2-intrinsics-for-msvc.patch b/ports/webrtc/webrtc-0009-fix-avx2-intrinsics-for-msvc.patch new file mode 100644 index 00000000000000..11dc6447e07906 --- /dev/null +++ b/ports/webrtc/webrtc-0009-fix-avx2-intrinsics-for-msvc.patch @@ -0,0 +1,57 @@ +diff --git a/modules/audio_processing/aec3/matched_filter_avx2.cc b/modules/audio_processing/aec3/matched_filter_avx2.cc +index eacd6486b1..b5c88d3b57 100644 +--- a/modules/audio_processing/aec3/matched_filter_avx2.cc ++++ b/modules/audio_processing/aec3/matched_filter_avx2.cc +@@ -82,14 +82,28 @@ void MatchedFilterCore_AccumulatedError_AVX2(size_t x_start_index, + s_inst_256_8 = _mm256_mul_ps(h_k_8, x_k_8); + s_inst_hadd_256 = _mm256_hadd_ps(s_inst_256, s_inst_256_8); + s_inst_hadd_256 = _mm256_hadd_ps(s_inst_hadd_256, s_inst_hadd_256); +- s_acum += s_inst_hadd_256[0]; +- e_128[0] = s_acum - y[i]; +- s_acum += s_inst_hadd_256[4]; +- e_128[1] = s_acum - y[i]; +- s_acum += s_inst_hadd_256[1]; +- e_128[2] = s_acum - y[i]; +- s_acum += s_inst_hadd_256[5]; +- e_128[3] = s_acum - y[i]; ++#ifdef _MSC_VER ++ alignas(32) float partial_sums[8]; ++ _mm256_storeu_ps(partial_sums, s_inst_hadd_256); ++ s_acum += partial_sums[0]; ++ const float e0 = s_acum - y[i]; ++ s_acum += partial_sums[4]; ++ const float e1 = s_acum - y[i]; ++ s_acum += partial_sums[1]; ++ const float e2 = s_acum - y[i]; ++ s_acum += partial_sums[5]; ++ const float e3 = s_acum - y[i]; ++ e_128 = _mm_setr_ps(e0, e1, e2, e3); ++#else ++ s_acum += s_inst_hadd_256[0]; ++ e_128[0] = s_acum - y[i]; ++ s_acum += s_inst_hadd_256[4]; ++ e_128[1] = s_acum - y[i]; ++ s_acum += s_inst_hadd_256[1]; ++ e_128[2] = s_acum - y[i]; ++ s_acum += s_inst_hadd_256[5]; ++ e_128[3] = s_acum - y[i]; ++#endif + + __m128 acum_error = _mm_loadu_ps(a_p); + acum_error = _mm_fmadd_ps(e_128, e_128, acum_error); +@@ -210,8 +224,13 @@ void MatchedFilterCore_AVX2(size_t x_start_index, + x2_sum_256 = _mm256_add_ps(x2_sum_256, x2_sum_256_8); + s_256 = _mm256_add_ps(s_256, s_256_8); + __m128 sum = hsum_ab(x2_sum_256, s_256); +- x2_sum += sum[0]; +- s += sum[1]; ++#ifdef _MSC_VER ++ x2_sum += _mm_cvtss_f32(sum); ++ s += _mm_cvtss_f32(_mm_shuffle_ps(sum, sum, _MM_SHUFFLE(1, 1, 1, 1))); ++#else ++ x2_sum += sum[0]; ++ s += sum[1]; ++#endif + + // Compute the matched filter error. + float e = y[i] - s; diff --git a/ports/webrtc/webrtc-0010-fix-denormal-disabler-for-msvc.patch b/ports/webrtc/webrtc-0010-fix-denormal-disabler-for-msvc.patch new file mode 100644 index 00000000000000..a458de9daab1ea --- /dev/null +++ b/ports/webrtc/webrtc-0010-fix-denormal-disabler-for-msvc.patch @@ -0,0 +1,64 @@ +diff --git a/rtc_base/denormal_disabler.cc b/rtc_base/denormal_disabler.cc +index 6f2ebb2857..ae0e0e4bfb 100644 +--- a/rtc_base/denormal_disabler.cc ++++ b/rtc_base/denormal_disabler.cc +@@ -10,18 +10,27 @@ + + #include "rtc_base/denormal_disabler.h" + ++#if defined(_MSC_VER) && \ ++ (defined(WEBRTC_ARCH_X86_FAMILY) || \ ++ (defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS))) ++#include ++#endif ++ + #include "rtc_base/checks.h" + #include "rtc_base/system/arch.h" + + namespace webrtc { + namespace { + +-#if defined(WEBRTC_ARCH_X86_FAMILY) && defined(__clang__) ++#if defined(WEBRTC_ARCH_X86_FAMILY) && \ ++ (defined(__clang__) || defined(_MSC_VER)) + #define WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED + #endif + + #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) || \ +- defined(WEBRTC_ARCH_ARM_FAMILY) ++ (defined(WEBRTC_ARCH_ARM_FAMILY) && \ ++ ((defined(__clang__) || defined(__GNUC__)) || \ ++ (defined(_MSC_VER) && defined(WEBRTC_ARCH_64_BITS)))) + #define WEBRTC_DENORMAL_DISABLER_SUPPORTED + #endif + +@@ -43,7 +52,14 @@ constexpr int kDenormalBitMask = 1 << 24; + int ReadStatusWord() { + int result = kUnspecifiedStatusWord; + #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) ++#if defined(_MSC_VER) ++ result = static_cast(_mm_getcsr()); ++#else + asm volatile("stmxcsr %0" : "=m"(result)); ++#endif ++#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) && \ ++ defined(_MSC_VER) ++ result = static_cast(_ReadStatusReg(ARM64_FPCR)); + #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS) + asm volatile("vmrs %[result], FPSCR" : [result] "=r"(result)); + #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) +@@ -56,7 +72,14 @@ int ReadStatusWord() { + // and the compiler are supported. + void SetStatusWord(int status_word) { + #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) ++#if defined(_MSC_VER) ++ _mm_setcsr(static_cast(status_word)); ++#else + asm volatile("ldmxcsr %0" : : "m"(status_word)); ++#endif ++#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) && \ ++ defined(_MSC_VER) ++ _WriteStatusReg(ARM64_FPCR, static_cast<__int64>(status_word)); + #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS) + asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(status_word)); + #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) diff --git a/ports/webrtc/webrtc-0011-make-linux-audio-backends-optional.patch b/ports/webrtc/webrtc-0011-make-linux-audio-backends-optional.patch new file mode 100644 index 00000000000000..0aee76f2a6ff9b --- /dev/null +++ b/ports/webrtc/webrtc-0011-make-linux-audio-backends-optional.patch @@ -0,0 +1,144 @@ +diff --git a/modules/audio_device/BUILD.gn b/modules/audio_device/BUILD.gn +--- a/modules/audio_device/BUILD.gn ++++ b/modules/audio_device/BUILD.gn +@@ -338,33 +338,37 @@ rtc_library("audio_device_impl") { + defines += [ "WEBRTC_DUMMY_FILE_DEVICES" ] + } else { + if (is_linux || is_chromeos) { +- sources += [ +- "linux/alsasymboltable_linux.cc", +- "linux/alsasymboltable_linux.h", +- "linux/audio_device_alsa_linux.cc", +- "linux/audio_device_alsa_linux.h", +- "linux/audio_mixer_manager_alsa_linux.cc", +- "linux/audio_mixer_manager_alsa_linux.h", +- "linux/latebindingsymboltable_linux.cc", +- "linux/latebindingsymboltable_linux.h", +- ] +- defines += [ "WEBRTC_ENABLE_LINUX_ALSA" ] + libs = [ "dl" ] + if (rtc_use_x11) { + libs += [ "X11" ] + defines += [ "WEBRTC_USE_X11" ] + } ++ if (rtc_include_alsa_audio) { ++ configs += [ "//third_party/alsa:headers" ] ++ sources += [ ++ "linux/alsasymboltable_linux.cc", ++ "linux/alsasymboltable_linux.h", ++ "linux/audio_device_alsa_linux.cc", ++ "linux/audio_device_alsa_linux.h", ++ "linux/audio_mixer_manager_alsa_linux.cc", ++ "linux/audio_mixer_manager_alsa_linux.h", ++ "linux/latebindingsymboltable_linux.cc", ++ "linux/latebindingsymboltable_linux.h", ++ ] ++ defines += [ "WEBRTC_ENABLE_LINUX_ALSA" ] ++ } + if (rtc_include_pulse_audio) { ++ configs += [ "//third_party/pulseaudio:headers" ] + defines += [ "WEBRTC_ENABLE_LINUX_PULSE" ] ++ sources += [ ++ "linux/audio_device_pulse_linux.cc", ++ "linux/audio_device_pulse_linux.h", ++ "linux/audio_mixer_manager_pulse_linux.cc", ++ "linux/audio_mixer_manager_pulse_linux.h", ++ "linux/pulseaudiosymboltable_linux.cc", ++ "linux/pulseaudiosymboltable_linux.h", ++ ] + } +- sources += [ +- "linux/audio_device_pulse_linux.cc", +- "linux/audio_device_pulse_linux.h", +- "linux/audio_mixer_manager_pulse_linux.cc", +- "linux/audio_mixer_manager_pulse_linux.h", +- "linux/pulseaudiosymboltable_linux.cc", +- "linux/pulseaudiosymboltable_linux.h", +- ] + } + if (is_mac) { + sources += [ +diff --git a/modules/audio_device/audio_device_impl.cc b/modules/audio_device/audio_device_impl.cc +--- a/modules/audio_device/audio_device_impl.cc ++++ b/modules/audio_device/audio_device_impl.cc +@@ -191,40 +191,41 @@ AudioDeviceModuleImpl::CreatePlatformSpecificObjects(const Environment& env) { + #endif + + // Linux ADM implementation. +-// Note that, WEBRTC_ENABLE_LINUX_ALSA is always defined by default when +-// WEBRTC_LINUX is defined. WEBRTC_ENABLE_LINUX_PULSE depends on the +-// 'rtc_include_pulse_audio' build flag. +-// TODO(bugs.webrtc.org/9127): improve support and make it more clear that +-// PulseAudio is the default selection. ++// WEBRTC_ENABLE_LINUX_ALSA depends on the 'rtc_include_alsa_audio' build flag. ++// WEBRTC_ENABLE_LINUX_PULSE depends on the 'rtc_include_pulse_audio' build ++// flag. PulseAudio remains the default selection when both backends are ++// enabled. + #if !defined(WEBRTC_ANDROID) && defined(WEBRTC_LINUX) +-#if !defined(WEBRTC_ENABLE_LINUX_PULSE) +- // Build flag 'rtc_include_pulse_audio' is set to false. In this mode: +- // - kPlatformDefaultAudio => ALSA, and +- // - kLinuxAlsaAudio => ALSA, and +- // - kLinuxPulseAudio => Invalid selection. +- RTC_LOG(LS_WARNING) << "PulseAudio is disabled using build flag."; +- if ((audio_layer == kLinuxAlsaAudio) || +- (audio_layer == kPlatformDefaultAudio)) { +- audio_device_.reset(new AudioDeviceLinuxALSA()); +- RTC_LOG(LS_INFO) << "Linux ALSA APIs will be utilized."; +- } +-#else +- // Build flag 'rtc_include_pulse_audio' is set to true (default). In this +- // mode: +- // - kPlatformDefaultAudio => PulseAudio, and +- // - kLinuxPulseAudio => PulseAudio, and +- // - kLinuxAlsaAudio => ALSA (supported but not default). ++#if defined(WEBRTC_ENABLE_LINUX_PULSE) + RTC_LOG(LS_INFO) << "PulseAudio support is enabled."; + if ((audio_layer == kLinuxPulseAudio) || + (audio_layer == kPlatformDefaultAudio)) { +- // Linux PulseAudio implementation is default. + audio_device_.reset(new AudioDeviceLinuxPulse()); + RTC_LOG(LS_INFO) << "Linux PulseAudio APIs will be utilized"; +- } else if (audio_layer == kLinuxAlsaAudio) { ++ } ++#endif ++ ++#if defined(WEBRTC_ENABLE_LINUX_ALSA) ++ if (!audio_device_ && ++ ((audio_layer == kLinuxAlsaAudio) || ++ (audio_layer == kPlatformDefaultAudio))) { + audio_device_.reset(new AudioDeviceLinuxALSA()); +- RTC_LOG(LS_WARNING) << "Linux ALSA APIs will be utilized."; ++ RTC_LOG(LS_INFO) << "Linux ALSA APIs will be utilized."; + } +-#endif // #if !defined(WEBRTC_ENABLE_LINUX_PULSE) ++#endif ++ ++#if !defined(WEBRTC_ENABLE_LINUX_ALSA) && !defined(WEBRTC_ENABLE_LINUX_PULSE) ++ RTC_LOG(LS_WARNING) ++ << "Linux audio backends are disabled; falling back to dummy audio."; ++ if (audio_layer == kPlatformDefaultAudio) { ++ audio_device_.reset(new AudioDeviceDummy()); ++ RTC_LOG(LS_INFO) << "Dummy Audio APIs will be utilized."; ++ } ++#elif !defined(WEBRTC_ENABLE_LINUX_PULSE) ++ RTC_LOG(LS_WARNING) << "PulseAudio is disabled using build flag."; ++#elif !defined(WEBRTC_ENABLE_LINUX_ALSA) ++ RTC_LOG(LS_WARNING) << "ALSA is disabled using build flag."; ++#endif + #endif // #if defined(WEBRTC_LINUX) + + // iOS ADM implementation. +diff --git a/webrtc.gni b/webrtc.gni +--- a/webrtc.gni ++++ b/webrtc.gni +@@ -274,6 +274,9 @@ declare_args() { + rtc_build_opus = !build_with_mozilla + rtc_build_ssl = !build_with_mozilla + ++ # Excluded in Chromium since its prerequisites don't require ALSA. ++ rtc_include_alsa_audio = !build_with_chromium ++ + # Excluded in Chromium since its prerequisites don't require Pulse Audio. + rtc_include_pulse_audio = !build_with_chromium + diff --git a/ports/webrtc/webrtc-functions.cmake b/ports/webrtc/webrtc-functions.cmake new file mode 100644 index 00000000000000..6c27dd02d05aa4 --- /dev/null +++ b/ports/webrtc/webrtc-functions.cmake @@ -0,0 +1,89 @@ +# Declare a named repo to fetch with vcpkg_from_git and stage into SOURCE_PATH. +function(declare_webrtc_repo name) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "DESTINATION;URL;REF;PATCHES_VAR" "") + if(NOT arg_DESTINATION OR NOT arg_URL OR NOT arg_REF) + message(FATAL_ERROR "Arguments DESTINATION, URL and REF are required.") + endif() + + set(declared_repos "${webrtc_declared_repos}") + list(APPEND declared_repos "${name}") + set(webrtc_repo_destination_${name} "${arg_DESTINATION}" PARENT_SCOPE) + set(webrtc_repo_url_${name} "${arg_URL}" PARENT_SCOPE) + set(webrtc_repo_ref_${name} "${arg_REF}" PARENT_SCOPE) + set(webrtc_repo_patches_var_${name} "${arg_PATCHES_VAR}" PARENT_SCOPE) + set(webrtc_declared_repos "${declared_repos}" PARENT_SCOPE) +endfunction() + +# Fetch and stage all declared repos beneath the given source root. +function(fetch_declared_webrtc_repos source_path) + foreach(name IN LISTS webrtc_declared_repos) + set(repo_patches) + set(patches_var "${webrtc_repo_patches_var_${name}}") + if(NOT patches_var STREQUAL "" AND DEFINED ${patches_var}) + set(repo_patches ${${patches_var}}) + endif() + + vcpkg_from_git( + OUT_SOURCE_PATH repo_source_path + URL "${webrtc_repo_url_${name}}" + REF "${webrtc_repo_ref_${name}}" + PATCHES ${repo_patches} + ) + + set(repo_target_path "${source_path}/${webrtc_repo_destination_${name}}") + get_filename_component(repo_target_parent "${repo_target_path}" DIRECTORY) + file(MAKE_DIRECTORY "${repo_target_parent}") + file(REMOVE_RECURSE "${repo_target_path}") + file(RENAME "${repo_source_path}" "${repo_target_path}") + endforeach() +endfunction() + +# Declare a generated third-party overlay to be emitted by generate_external_dep(). +function(declare_webrtc_generated_external name) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "LIB_ROOT_VAR;TOOL_PATH_VAR;PHASE" "") + + set(declared_generated_externals "${webrtc_declared_generated_externals}") + list(APPEND declared_generated_externals "${name}") + set(webrtc_generated_external_lib_root_var_${name} "${arg_LIB_ROOT_VAR}" PARENT_SCOPE) + set(webrtc_generated_external_tool_path_var_${name} "${arg_TOOL_PATH_VAR}" PARENT_SCOPE) + if("${arg_PHASE}" STREQUAL "") + set(arg_PHASE "post_absl") + endif() + set(webrtc_generated_external_phase_${name} "${arg_PHASE}" PARENT_SCOPE) + set(webrtc_declared_generated_externals + "${declared_generated_externals}" PARENT_SCOPE) +endfunction() + +# Emit all declared generated third-party overlays for the current build config. +function(generate_declared_webrtc_externals source_path build_config phase) + foreach(name IN LISTS webrtc_declared_generated_externals) + if(NOT webrtc_generated_external_phase_${name} STREQUAL "${phase}") + continue() + endif() + + set(lib_root "${CURRENT_INSTALLED_DIR}/lib") + set(tool_path) + + set(lib_root_var "${webrtc_generated_external_lib_root_var_${name}}") + if(NOT lib_root_var STREQUAL "" AND DEFINED ${lib_root_var}) + set(lib_root "${${lib_root_var}}") + endif() + + set(tool_path_var "${webrtc_generated_external_tool_path_var_${name}}") + if(NOT tool_path_var STREQUAL "" AND DEFINED ${tool_path_var}) + set(tool_path "${${tool_path_var}}") + endif() + + if(tool_path STREQUAL "") + generate_external_dep( + "${source_path}" "${name}" "${CURRENT_INSTALLED_DIR}/include" + "${lib_root}" "${build_config}" + ) + else() + generate_external_dep( + "${source_path}" "${name}" "${CURRENT_INSTALLED_DIR}/include" + "${lib_root}" "${build_config}" TOOL_PATH "${tool_path}" + ) + endif() + endforeach() +endfunction() diff --git a/scripts/test_ports/vcpkg-ci-webrtc/portfile.cmake b/scripts/test_ports/vcpkg-ci-webrtc/portfile.cmake new file mode 100644 index 00000000000000..673231db0be795 --- /dev/null +++ b/scripts/test_ports/vcpkg-ci-webrtc/portfile.cmake @@ -0,0 +1,27 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) + +vcpkg_cmake_configure( + SOURCE_PATH "${CURRENT_PORT_DIR}/project" +) +vcpkg_cmake_install() + +# Run the sample only on the native CI triplets we explicitly want runtime +# coverage for. Other supported triplets stay build-only for now. +if(NOT VCPKG_CROSSCOMPILING AND + (TARGET_TRIPLET STREQUAL "arm64-osx" OR + TARGET_TRIPLET STREQUAL "x64-linux" OR + TARGET_TRIPLET STREQUAL "x64-windows")) + if(CMAKE_HOST_WIN32) + vcpkg_host_path_list(PREPEND ENV{PATH} "${CURRENT_INSTALLED_DIR}/bin") + elseif(CMAKE_HOST_APPLE) + vcpkg_host_path_list(PREPEND ENV{DYLD_LIBRARY_PATH} "${CURRENT_INSTALLED_DIR}/lib") + else() + vcpkg_host_path_list(PREPEND ENV{LD_LIBRARY_PATH} "${CURRENT_INSTALLED_DIR}/lib") + endif() + + vcpkg_execute_required_process( + COMMAND "${CURRENT_PACKAGES_DIR}/bin/${PORT}/main" + WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}" + LOGNAME runtime-${TARGET_TRIPLET} + ) +endif() diff --git a/scripts/test_ports/vcpkg-ci-webrtc/project/CMakeLists.txt b/scripts/test_ports/vcpkg-ci-webrtc/project/CMakeLists.txt new file mode 100644 index 00000000000000..189db34a11a34a --- /dev/null +++ b/scripts/test_ports/vcpkg-ci-webrtc/project/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.20) + +project(webrtc_ci_test LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(unofficial-webrtc CONFIG REQUIRED) + +add_executable(main main.cc) +target_link_libraries(main PRIVATE unofficial::webrtc::webrtc) + +install(TARGETS main RUNTIME DESTINATION bin/vcpkg-ci-webrtc) diff --git a/scripts/test_ports/vcpkg-ci-webrtc/project/main.cc b/scripts/test_ports/vcpkg-ci-webrtc/project/main.cc new file mode 100644 index 00000000000000..27b6ee036fb5a3 --- /dev/null +++ b/scripts/test_ports/vcpkg-ci-webrtc/project/main.cc @@ -0,0 +1,582 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/cleanup/cleanup.h" +#include "api/create_modular_peer_connection_factory.h" +#include "api/enable_media_with_defaults.h" +#include "api/environment/environment_factory.h" +#include "api/jsep.h" +#include "api/make_ref_counted.h" +#include "api/media_stream_interface.h" +#include "api/notifier.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" +#include "api/set_local_description_observer_interface.h" +#include "api/set_remote_description_observer_interface.h" +#include "api/video/i420_buffer.h" +#include "api/video/video_frame.h" +#include "api/video/video_sink_interface.h" +#include "modules/audio_device/include/fake_audio_device.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/ssl_adapter.h" +#include "rtc_base/thread.h" +#if defined(WEBRTC_WIN) +#include "rtc_base/win32_socket_init.h" +#endif + +namespace { + +using Clock = std::chrono::steady_clock; + +class SyntheticVideoSource + : public webrtc::Notifier { + public: + SyntheticVideoSource() + : state_(kLive), producer_([this] { + uint32_t frame_index = 0; + while (!stop_.load()) { + BroadcastFrame(frame_index++); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }) {} + + ~SyntheticVideoSource() override { + stop_.store(true); + if (producer_.joinable()) { + producer_.join(); + } + state_ = kEnded; + } + + SourceState state() const override { return state_; } + bool remote() const override { return false; } + bool is_screencast() const override { return false; } + std::optional needs_denoising() const override { return false; } + bool GetStats(Stats* stats) override { + if (stats == nullptr) { + return false; + } + stats->input_width = kWidth; + stats->input_height = kHeight; + return true; + } + bool SupportsEncodedOutput() const override { return false; } + void GenerateKeyFrame() override {} + void AddEncodedSink( + webrtc::VideoSinkInterface* sink) + override {} + void RemoveEncodedSink( + webrtc::VideoSinkInterface* sink) + override {} + + void AddOrUpdateSink(webrtc::VideoSinkInterface* sink, + const webrtc::VideoSinkWants& wants) override { + std::lock_guard lock(mutex_); + for (auto& entry : sinks_) { + if (entry.sink == sink) { + entry.wants = wants; + return; + } + } + sinks_.push_back({sink, wants}); + } + + void RemoveSink( + webrtc::VideoSinkInterface* sink) override { + std::lock_guard lock(mutex_); + sinks_.erase(std::remove_if(sinks_.begin(), sinks_.end(), + [sink](const SinkEntry& entry) { + return entry.sink == sink; + }), + sinks_.end()); + } + + private: + struct SinkEntry { + webrtc::VideoSinkInterface* sink; + webrtc::VideoSinkWants wants; + }; + + static constexpr int kWidth = 640; + static constexpr int kHeight = 480; + + void BroadcastFrame(uint32_t frame_index) { + auto buffer = webrtc::I420Buffer::Create(kWidth, kHeight); + FillFrame(*buffer, frame_index); + + const int64_t timestamp_us = + std::chrono::duration_cast( + Clock::now().time_since_epoch()) + .count(); + + webrtc::VideoFrame frame = webrtc::VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_timestamp_us(timestamp_us) + .set_rotation(webrtc::kVideoRotation_0) + .build(); + + std::vector sinks_copy; + { + std::lock_guard lock(mutex_); + sinks_copy = sinks_; + } + + for (const SinkEntry& entry : sinks_copy) { + if (entry.sink != nullptr) { + entry.sink->OnFrame(frame); + } + } + } + + static void FillPlane(uint8_t* data, + int stride, + int width, + int height, + uint8_t value) { + for (int y = 0; y < height; ++y) { + std::memset(data + y * stride, value, static_cast(width)); + } + } + + static void FillFrame(webrtc::I420Buffer& buffer, uint32_t frame_index) { + const uint8_t luma = static_cast((frame_index * 7) % 255); + const uint8_t chroma_u = + static_cast(64 + ((frame_index * 5) % 128)); + const uint8_t chroma_v = + static_cast(192 - ((frame_index * 3) % 128)); + + FillPlane(buffer.MutableDataY(), buffer.StrideY(), buffer.width(), + buffer.height(), luma); + FillPlane(buffer.MutableDataU(), buffer.StrideU(), (buffer.width() + 1) / 2, + (buffer.height() + 1) / 2, chroma_u); + FillPlane(buffer.MutableDataV(), buffer.StrideV(), (buffer.width() + 1) / 2, + (buffer.height() + 1) / 2, chroma_v); + } + + SourceState state_; + std::atomic stop_{false}; + std::mutex mutex_; + std::vector sinks_; + std::thread producer_; +}; + +class FrameCounterSink final + : public webrtc::VideoSinkInterface { + public: + explicit FrameCounterSink(int target_frames) + : target_frames_(target_frames) {} + + void OnFrame(const webrtc::VideoFrame& frame) override { + std::lock_guard lock(mutex_); + if (frame_count_ == 0) { + first_timestamp_us_ = frame.timestamp_us(); + } + last_timestamp_us_ = frame.timestamp_us(); + ++frame_count_; + RTC_LOG(LS_INFO) << "remote frame " << frame_count_ + << " ts_us=" << frame.timestamp_us(); + if (frame_count_ >= target_frames_) { + done_ = true; + cv_.notify_all(); + } + } + + bool WaitForFrames(std::chrono::seconds timeout) { + std::unique_lock lock(mutex_); + return cv_.wait_for(lock, timeout, [this] { return done_; }); + } + + int frame_count() const { + std::lock_guard lock(mutex_); + return frame_count_; + } + + private: + const int target_frames_; + mutable std::mutex mutex_; + std::condition_variable cv_; + int frame_count_ = 0; + int64_t first_timestamp_us_ = 0; + int64_t last_timestamp_us_ = 0; + bool done_ = false; +}; + +class CreateDescriptionObserver + : public webrtc::CreateSessionDescriptionObserver { + public: + void OnSuccess(webrtc::SessionDescriptionInterface* desc) override { + std::lock_guard lock(mutex_); + description_.reset(desc); + done_ = true; + cv_.notify_all(); + } + + void OnFailure(webrtc::RTCError error) override { + std::lock_guard lock(mutex_); + error_ = error.message(); + done_ = true; + cv_.notify_all(); + } + + std::unique_ptr Wait() { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return done_; }); + if (!error_.empty()) { + RTC_LOG(LS_ERROR) << "CreateSessionDescription failed: " << error_; + return nullptr; + } + return std::move(description_); + } + + private: + std::mutex mutex_; + std::condition_variable cv_; + bool done_ = false; + std::string error_; + std::unique_ptr description_; +}; + +class SetLocalObserver : public webrtc::SetLocalDescriptionObserverInterface { + public: + void OnSetLocalDescriptionComplete(webrtc::RTCError error) override { + std::lock_guard lock(mutex_); + error_ = std::move(error); + done_ = true; + cv_.notify_all(); + } + + bool Wait() { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return done_; }); + if (!error_.ok()) { + RTC_LOG(LS_ERROR) << "SetLocalDescription failed: " << error_.message(); + } + return error_.ok(); + } + + private: + std::mutex mutex_; + std::condition_variable cv_; + bool done_ = false; + webrtc::RTCError error_ = webrtc::RTCError::OK(); +}; + +class SetRemoteObserver : public webrtc::SetRemoteDescriptionObserverInterface { + public: + void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override { + std::lock_guard lock(mutex_); + error_ = std::move(error); + done_ = true; + cv_.notify_all(); + } + + bool Wait() { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return done_; }); + if (!error_.ok()) { + RTC_LOG(LS_ERROR) << "SetRemoteDescription failed: " << error_.message(); + } + return error_.ok(); + } + + private: + std::mutex mutex_; + std::condition_variable cv_; + bool done_ = false; + webrtc::RTCError error_ = webrtc::RTCError::OK(); +}; + +class PeerObserver; + +class PeerHarness { + public: + PeerHarness(std::string name, FrameCounterSink* remote_sink) + : name_(std::move(name)), + observer_(std::make_unique(*this, remote_sink)) {} + + bool Initialize(webrtc::PeerConnectionFactoryInterface& factory); + bool AddSyntheticTrack(webrtc::PeerConnectionFactoryInterface& factory); + bool SetRemoteDescriptionCopy(webrtc::SdpType type, const std::string& sdp); + bool AddIceCandidateCopy(const webrtc::IceCandidate& candidate); + + webrtc::PeerConnectionInterface* connection() const { + return connection_.get(); + } + void set_remote(PeerHarness* remote) { remote_ = remote; } + + private: + class PeerObserver final : public webrtc::PeerConnectionObserver { + public: + PeerObserver(PeerHarness& owner, FrameCounterSink* remote_sink) + : owner_(owner), remote_sink_(remote_sink) {} + + void OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state) override { + RTC_LOG(LS_INFO) << owner_.name_ << " signaling=" + << webrtc::PeerConnectionInterface::AsString(new_state); + } + + void OnDataChannel(webrtc::scoped_refptr + data_channel) override { + (void)data_channel; + } + + void OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) override { + RTC_LOG(LS_INFO) << owner_.name_ << " ice_gathering=" + << webrtc::PeerConnectionInterface::AsString(new_state); + } + + void OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState + new_state) override { + RTC_LOG(LS_INFO) << owner_.name_ << " connection_state=" + << webrtc::PeerConnectionInterface::AsString(new_state); + } + + void OnIceCandidate(const webrtc::IceCandidate* candidate) override { + if (candidate == nullptr || owner_.remote_ == nullptr) { + return; + } + if (!owner_.remote_->AddIceCandidateCopy(*candidate)) { + RTC_LOG(LS_ERROR) << owner_.name_ << " failed to forward ICE candidate"; + } + } + + void OnTrack(webrtc::scoped_refptr + transceiver) override { + if (remote_sink_ == nullptr) { + return; + } + auto track = transceiver->receiver()->track(); + if (track && + track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { + static_cast(track.get()) + ->AddOrUpdateSink(remote_sink_, webrtc::VideoSinkWants()); + RTC_LOG(LS_INFO) << owner_.name_ << " attached remote video sink"; + } + } + + private: + PeerHarness& owner_; + FrameCounterSink* remote_sink_; + }; + + std::string name_; + std::unique_ptr observer_; + PeerHarness* remote_ = nullptr; + webrtc::scoped_refptr connection_; + webrtc::scoped_refptr source_; +}; + +bool PeerHarness::Initialize(webrtc::PeerConnectionFactoryInterface& factory) { + webrtc::PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; + + webrtc::PeerConnectionDependencies deps(observer_.get()); + auto result = factory.CreatePeerConnectionOrError(config, std::move(deps)); + if (!result.ok()) { + RTC_LOG(LS_ERROR) << name_ << " CreatePeerConnection failed: " + << result.error().message(); + return false; + } + connection_ = result.MoveValue(); + return true; +} + +bool PeerHarness::AddSyntheticTrack( + webrtc::PeerConnectionFactoryInterface& factory) { + source_ = webrtc::make_ref_counted(); + auto track = factory.CreateVideoTrack(source_, "synthetic-video"); + auto result = connection_->AddTrack(track, {"synthetic-stream"}); + if (!result.ok()) { + RTC_LOG(LS_ERROR) << name_ + << " AddTrack failed: " << result.error().message(); + return false; + } + return true; +} + +bool PeerHarness::SetRemoteDescriptionCopy(webrtc::SdpType type, + const std::string& sdp) { + webrtc::SdpParseError error; + auto desc = webrtc::CreateSessionDescription(type, sdp, &error); + if (!desc) { + RTC_LOG(LS_ERROR) << name_ + << " failed to parse remote SDP: " << error.description; + return false; + } + auto observer = webrtc::make_ref_counted(); + connection_->SetRemoteDescription(std::move(desc), observer); + return observer->Wait(); +} + +bool PeerHarness::AddIceCandidateCopy(const webrtc::IceCandidate& candidate) { + auto copy = webrtc::CreateIceCandidate(candidate.sdp_mid(), + candidate.sdp_mline_index(), + candidate.ToString(), nullptr); + return copy != nullptr && connection_->AddIceCandidate(copy); +} + +bool ApplyLocalDescription( + webrtc::PeerConnectionInterface& connection, + std::unique_ptr desc) { + auto observer = webrtc::make_ref_counted(); + connection.SetLocalDescription(std::move(desc), observer); + return observer->Wait(); +} + +std::unique_ptr CreateOffer( + webrtc::PeerConnectionInterface& connection) { + auto observer = webrtc::make_ref_counted(); + connection.CreateOffer(observer.get(), {}); + return observer->Wait(); +} + +std::unique_ptr CreateAnswer( + webrtc::PeerConnectionInterface& connection) { + auto observer = webrtc::make_ref_counted(); + connection.CreateAnswer(observer.get(), {}); + return observer->Wait(); +} + +bool Negotiate(PeerHarness& caller, PeerHarness& callee) { + auto offer = CreateOffer(*caller.connection()); + if (!offer) { + return false; + } + + std::string offer_sdp; + if (!offer->ToString(&offer_sdp)) { + RTC_LOG(LS_ERROR) << "failed to serialize offer"; + return false; + } + + if (!ApplyLocalDescription(*caller.connection(), std::move(offer))) { + return false; + } + if (!callee.SetRemoteDescriptionCopy(webrtc::SdpType::kOffer, offer_sdp)) { + return false; + } + + auto answer = CreateAnswer(*callee.connection()); + if (!answer) { + return false; + } + + std::string answer_sdp; + if (!answer->ToString(&answer_sdp)) { + RTC_LOG(LS_ERROR) << "failed to serialize answer"; + return false; + } + + if (!ApplyLocalDescription(*callee.connection(), std::move(answer))) { + return false; + } + if (!caller.SetRemoteDescriptionCopy(webrtc::SdpType::kAnswer, answer_sdp)) { + return false; + } + + return true; +} + +} // namespace + +int main() { + webrtc::LogMessage::SetLogToStderr(true); + webrtc::LogMessage::LogToDebug(webrtc::LS_INFO); + webrtc::LogMessage::LogTimestamps(); + webrtc::LogMessage::LogThreads(); + +#if defined(WEBRTC_WIN) + webrtc::WinsockInitializer winsock_init; + if (winsock_init.error() != 0) { + RTC_LOG(LS_ERROR) << "failed to initialize Winsock: " + << winsock_init.error(); + return 1; + } +#endif + if (!webrtc::InitializeSSL()) { + RTC_LOG(LS_ERROR) << "failed to initialize SSL"; + return 1; + } + auto ssl_cleanup = absl::Cleanup([] { webrtc::CleanupSSL(); }); + + auto network_thread = webrtc::Thread::CreateWithSocketServer(); + auto worker_thread = webrtc::Thread::Create(); + auto signaling_thread = webrtc::Thread::Create(); + + network_thread->SetName("webrtc-sample-network", nullptr); + worker_thread->SetName("webrtc-sample-worker", nullptr); + signaling_thread->SetName("webrtc-sample-signaling", nullptr); + + if (!network_thread->Start() || !worker_thread->Start() || + !signaling_thread->Start()) { + RTC_LOG(LS_ERROR) << "failed to start WebRTC threads"; + return 1; + } + + webrtc::PeerConnectionFactoryDependencies deps; + deps.env = webrtc::CreateEnvironment(); + deps.network_thread = network_thread.get(); + deps.worker_thread = worker_thread.get(); + deps.signaling_thread = signaling_thread.get(); + webrtc::EnableMediaWithDefaults(deps); + deps.adm = webrtc::scoped_refptr( + new webrtc::RefCountedObject()); + + auto factory = webrtc::CreateModularPeerConnectionFactory(std::move(deps)); + if (!factory) { + RTC_LOG(LS_ERROR) << "failed to create PeerConnectionFactory"; + return 1; + } + + webrtc::PeerConnectionFactoryInterface::Options options; + factory->SetOptions(options); + + FrameCounterSink remote_sink(10); + PeerHarness caller("caller", nullptr); + PeerHarness callee("callee", &remote_sink); + auto close_peers = absl::Cleanup([&] { + if (caller.connection() != nullptr) { + caller.connection()->Close(); + } + if (callee.connection() != nullptr) { + callee.connection()->Close(); + } + }); + caller.set_remote(&callee); + callee.set_remote(&caller); + + if (!caller.Initialize(*factory) || !callee.Initialize(*factory)) { + return 1; + } + if (!caller.AddSyntheticTrack(*factory)) { + return 1; + } + if (!Negotiate(caller, callee)) { + return 1; + } + + if (!remote_sink.WaitForFrames(std::chrono::seconds(10))) { + RTC_LOG(LS_ERROR) << "timed out waiting for remote video; frames=" + << remote_sink.frame_count(); + return 1; + } + + RTC_LOG(LS_INFO) << "success: remote frames=" << remote_sink.frame_count(); + return 0; +} diff --git a/scripts/test_ports/vcpkg-ci-webrtc/vcpkg.json b/scripts/test_ports/vcpkg-ci-webrtc/vcpkg.json new file mode 100644 index 00000000000000..d4431e6ff6746d --- /dev/null +++ b/scripts/test_ports/vcpkg-ci-webrtc/vcpkg.json @@ -0,0 +1,15 @@ +{ + "name": "vcpkg-ci-webrtc", + "version-string": "ci", + "description": "Validates webrtc", + "homepage": "https://github.com/microsoft/vcpkg", + "license": "MIT", + "supports": "(linux & (arm64 | x64)) | (osx & (arm64 | x64)) | (windows & (arm64 | x86 | x64))", + "dependencies": [ + "webrtc", + { + "name": "vcpkg-cmake", + "host": true + } + ] +} diff --git a/versions/baseline.json b/versions/baseline.json index 079e8a818fea60..0a2ca713c04e70 100644 --- a/versions/baseline.json +++ b/versions/baseline.json @@ -10752,6 +10752,10 @@ "baseline": "1.0.5", "port-version": 0 }, + "webrtc": { + "baseline": "2026-03-17", + "port-version": 0 + }, "webthing-cpp": { "baseline": "1.2.0", "port-version": 0 diff --git a/versions/w-/webrtc.json b/versions/w-/webrtc.json new file mode 100644 index 00000000000000..0fb138246e41f0 --- /dev/null +++ b/versions/w-/webrtc.json @@ -0,0 +1,9 @@ +{ + "versions": [ + { + "git-tree": "cef33e288ddedf719feb03fc55040774d6edea39", + "version-date": "2026-03-17", + "port-version": 0 + } + ] +}