Use source_sets in component builds and static_library in release builds.

Static libraries don't guarantee that an exported symbol gets linked
into a shared library (and in order to support Chromium's component
build mode, WebRTC needs to be linked as a shared library).

Source sets always pass all the object files to the linker.

On the flip side, source_sets link more object files in release builds
and to avoid this, this CL introduces a the GN template "rtc_library" that
expands to static_library during release builds and to source_set during
component builds.

See: https://gn.googlesource.com/gn/+/master/docs/reference.md#func_source_set

Bug: webrtc:9419
Change-Id: I4667e820c2b3fcec417becbd2034acc13e4f04fe
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/157168
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Nico Weber <thakis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#29525}
diff --git a/webrtc.gni b/webrtc.gni
index 2e6ccc8..6033764 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -300,17 +300,22 @@
   suppressed_configs = []
 }
 
+set_defaults("rtc_library") {
+  configs = rtc_add_configs
+  suppressed_configs = []
+}
+
 set_defaults("rtc_source_set") {
   configs = rtc_add_configs
   suppressed_configs = []
 }
 
-set_defaults("rtc_executable") {
+set_defaults("rtc_static_library") {
   configs = rtc_add_configs
   suppressed_configs = []
 }
 
-set_defaults("rtc_static_library") {
+set_defaults("rtc_executable") {
   configs = rtc_add_configs
   suppressed_configs = []
 }
@@ -485,46 +490,6 @@
   }
 }
 
-template("rtc_executable") {
-  executable(target_name) {
-    forward_variables_from(invoker,
-                           "*",
-                           [
-                             "deps",
-                             "configs",
-                             "public_configs",
-                             "suppressed_configs",
-                             "visibility",
-                           ])
-    forward_variables_from(invoker, [ "visibility" ])
-    if (!defined(visibility)) {
-      visibility = webrtc_default_visibility
-    }
-    configs += invoker.configs
-    configs -= rtc_remove_configs
-    configs -= invoker.suppressed_configs
-    deps = invoker.deps
-
-    public_configs = [
-      rtc_common_inherited_config,
-      absl_include_config,
-      absl_define_config,
-    ]
-    if (defined(testonly) && testonly) {
-      public_configs += [ absl_flags_config ]
-    }
-    if (defined(invoker.public_configs)) {
-      public_configs += invoker.public_configs
-    }
-    if (is_win) {
-      deps += [
-        # Give executables the default manifest on Windows (a no-op elsewhere).
-        "//build/win:default_exe_manifest",
-      ]
-    }
-  }
-}
-
 template("rtc_static_library") {
   static_library(target_name) {
     forward_variables_from(invoker,
@@ -600,6 +565,143 @@
   }
 }
 
+# This template automatically switches the target type between source_set
+# and static_library.
+#
+# This should be the default target type for all the WebRTC targets with
+# one exception. Do not use this template for header only targets, in that case
+# rtc_source_set must be used in order to avoid build errors (e.g. libtool
+# complains if the output .a file is empty).
+#
+# How does it work:
+# Since all files in a source_set are linked into a final binary, while files
+# in a static library are only linked in if at least one symbol in them is
+# referenced, in component builds source_sets are easy to deal with because
+# all their object files are passed to the linker to create a shared library.
+# In release builds instead, static_libraries are preferred since they allow
+# the linker to discard dead code.
+# For the same reason, testonly targets will always be expanded to
+# source_set in order to be sure that tests are present in the test binary.
+template("rtc_library") {
+  if (is_component_build || (defined(invoker.testonly) && invoker.testonly)) {
+    target_type = "source_set"
+  } else {
+    target_type = "static_library"
+  }
+  target(target_type, target_name) {
+    forward_variables_from(invoker,
+                           "*",
+                           [
+                             "configs",
+                             "public_configs",
+                             "suppressed_configs",
+                             "visibility",
+                           ])
+    forward_variables_from(invoker, [ "visibility" ])
+    if (!defined(visibility)) {
+      visibility = webrtc_default_visibility
+    }
+
+    # What's your poison?
+    if (defined(testonly) && testonly) {
+      assert(!defined(poisonous))
+      assert(!defined(allow_poison))
+    } else {
+      if (!defined(poisonous)) {
+        poisonous = []
+      }
+      if (!defined(allow_poison)) {
+        allow_poison = []
+      }
+      if (!defined(assert_no_deps)) {
+        assert_no_deps = []
+      }
+      if (!defined(deps)) {
+        deps = []
+      }
+      foreach(p, poisonous) {
+        deps += [ webrtc_root + ":poison_" + p ]
+      }
+      foreach(poison_type, all_poison_types) {
+        allow_dep = true
+        foreach(v, visibility) {
+          if (v == "*") {
+            allow_dep = false
+          }
+        }
+        foreach(p, allow_poison + poisonous) {
+          if (p == poison_type) {
+            allow_dep = true
+          }
+        }
+        if (!allow_dep) {
+          assert_no_deps += [ webrtc_root + ":poison_" + poison_type ]
+        }
+      }
+    }
+
+    if (!defined(testonly) || !testonly) {
+      configs += rtc_prod_configs
+    }
+
+    configs += invoker.configs
+    configs += rtc_library_impl_config
+    configs -= rtc_remove_configs
+    configs -= invoker.suppressed_configs
+    public_configs = [
+      rtc_common_inherited_config,
+      absl_include_config,
+      absl_define_config,
+    ]
+    if (defined(testonly) && testonly) {
+      public_configs += [ absl_flags_config ]
+    }
+    if (defined(invoker.public_configs)) {
+      public_configs += invoker.public_configs
+    }
+  }
+}
+
+template("rtc_executable") {
+  executable(target_name) {
+    forward_variables_from(invoker,
+                           "*",
+                           [
+                             "deps",
+                             "configs",
+                             "public_configs",
+                             "suppressed_configs",
+                             "visibility",
+                           ])
+    forward_variables_from(invoker, [ "visibility" ])
+    if (!defined(visibility)) {
+      visibility = webrtc_default_visibility
+    }
+    configs += invoker.configs
+    configs -= rtc_remove_configs
+    configs -= invoker.suppressed_configs
+    deps = invoker.deps
+
+    public_configs = [
+      rtc_common_inherited_config,
+      absl_include_config,
+      absl_define_config,
+    ]
+    if (defined(testonly) && testonly) {
+      public_configs += [ absl_flags_config ]
+    }
+    if (defined(invoker.public_configs)) {
+      public_configs += invoker.public_configs
+    }
+    if (is_win) {
+      deps += [
+        # Give executables the default manifest on Windows (a no-op elsewhere).
+        "//build/win:default_exe_manifest",
+      ]
+    }
+  }
+}
+
 template("rtc_shared_library") {
   shared_library(target_name) {
     forward_variables_from(invoker,