chromium/third_party/grpc/template/BUILD.chromium.gn.template

%YAML 1.2
--- |
  <%doc>
  Header piece
  </%doc>\
  # GRPC Chromium GN build file

  # This file has been automatically generated from a template file.
  # Please look at the templates directory instead.
  # See //third_party/grpc/README.chromium for more information.

  declare_args() {
    # Compiles with ares.
    enable_grpc_ares = false

    # TODO(169395837): Somehow gRPC symbols cannot be found on Android.
    # Keep using static linking for now.
    # In windows, mac and iOS use static linking.
    # Use static linking on Chrome OS as a workaround for the symbol lookup
    # error(crbug/1241330) due to a gRPC version mismatch between what Chrome
    # uses and what CrOS provides.
    grpc_use_static_linking =
        is_android || is_win || is_chromeos || is_mac || is_ios
  }

  if (is_android) {
    import("//build/config/android/config.gni")
    import("//build/config/android/rules.gni")
  }

  config("grpc_config") {
    include_dirs = [
      "src/include",
      "src",
      "src/src/core/ext/upb-generated",
      "src/src/core/ext/upbdefs-generated",
      "src/third_party/cares",
      "//third_party/cares/include",
      "src/third_party/upb",
      "//third_party/abseil-cpp",
    ]

    defines = [
      "GRPC_USE_PROTO_LITE",
      "HAVE_CONFIG_H",
      "PB_FIELD_16BIT",
      "GRPC_NO_XDS",
      "GRPC_NO_RLS",
    ]

    if (!is_android) {
      # This prevents android specific object files from getting
      # included in shared library built for other platforms
      defines += [ "GRPC_NO_BINDER", ]
    }

    if (is_android) {
      libs = [ "log" ]  # For __android_log_write
    }

    if (is_android) {
      include_dirs += [ "src/third_party/cares/config_android" ]
    } else if (is_fuchsia) {
      include_dirs += [ "third_party/cares/config_fuchsia" ]
    } else {
      include_dirs += [ "src/third_party/cares/config_linux" ]
    }

    if (is_fuchsia) {
      defines += [
        # Allows zircon sockets to use file descriptors with gRPC.
        "GPR_SUPPORT_CHANNELS_FROM_FD",
      ]
    }

    if (!enable_grpc_ares) {
      defines += [
        # Disable c-ares since it doesn't currently support Fuchsia
        "GRPC_ARES=0",
      ]
    }
  }

  config("grpc_config_private") {
    # TODO(b/311287092): Clean up the code and remove this list.
    cflags = [
      "-Wno-c++98-compat-extra-semi",
      "-Wno-deprecated-copy",
      "-Wno-extra-semi",
      "-Wno-implicit-fallthrough",
      "-Wno-shadow",
      "-Wno-sign-compare",
      "-Wno-unreachable-code",
      "-Wno-unreachable-code-break",
      "-Wno-unreachable-code-return",
    ]

    # TODO(b/260740023): Remove when gRPC has CFI checks enabled.
    if (is_chromeos) {
      cflags += [ "-fno-sanitize=cfi-derived-cast,cfi-unrelated-cast" ]
    }
  }

  template("grpc_so") {
    if (grpc_use_static_linking) {
      source_set(target_name) {
        forward_variables_from(invoker, "*")
      }
    } else {
      shared_library(target_name) {
        forward_variables_from(invoker, "*")
        inputs = [ "./grpc_shared_lib.map" ]
        ldflags = [ "-Wl,--version-script=" + rebase_path("./grpc_shared_lib.map", root_build_dir) ]
      }
    }
  }
  <%doc>
  Python convenience functions.
  </%doc>
  <%!
  import os
  import re
  import glob

  # Sort list of sources or dependencies in a GN target.
  def gn_sort(l):
    new_l = []
    l = set(l)
    for i in l:
      if i.startswith(':'):
        new_l.append('1_{}'.format(i))
      elif i.startswith('//'):
        new_l.append('3_{}'.format(i))
      else:
        new_l.append('2_{}'.format(i))
    new_l.sort()
    return [i[2:] for i in new_l]

  # Find repeated basenames to avoid conflicts in GN.
  # Split the sources into 3 set of sources so that
  # sources with repeated basenames are in different sets
  def find_repeated(sources):
    # Deduplicate the sources files in the input so the same source file
    # don't appear in multiple sets
    sources = set(sources)
    # Convert sources to sorted list so the sources are split into 3 targets deterministically
    sources = sorted(sources)
    out_sources = []
    repeated1 = []
    repeated2 = []
    repeated3 = []
    repeated4 = []
    out_sources_basenames = set()
    repeated1_basenames = set()
    repeated2_basenames = set()
    repeated3_basenames = set()
    repeated4_basenames = set()
    for s in sources:
      basename = os.path.basename(s)
      ext = os.path.splitext(basename)
      if (len(ext) > 1) and (ext[1] == '.h') and not re.search('ext/upb', s):
        out_sources.append(s)
        continue
      if basename in out_sources_basenames:
        if basename in repeated3_basenames:
          # If there is a basename that appears more than 5 times,
          # we need to create more GN targets for it
          assert(s not in repeated4_basenames)
          repeated4_basenames.add(basename)
          repeated4.append(s)
        elif basename in repeated2_basenames:
          assert(s not in repeated3_basenames)
          repeated3_basenames.add(basename)
          repeated3.append(s)
        elif basename in repeated1_basenames:
          assert(s not in repeated2_basenames)
          repeated2_basenames.add(basename)
          repeated2.append(s)
        else:
          repeated1_basenames.add(basename)
          repeated1.append(s)
      else:
        out_sources_basenames.add(basename)
        out_sources.append(s)
    return (gn_sort(out_sources), gn_sort(repeated1), gn_sort(repeated2),
            gn_sort(repeated3), gn_sort(repeated4))

  def is_xds_source(s):
    file_names = [
        'src/src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc',
        'src/src/core/ext/xds/xds_server_config_fetcher.cc',
        'src/src/core/lib/security/credentials/xds/xds_credentials.cc',
        'src/src/core/lib/security/credentials/xds/xds_credentials.h',
        'src/src/cpp/client/xds_credentials.cc',
        'src/src/cpp/client/xds_credentials.cc',
        'src/src/cpp/client/xds_credentials.h',
        'src/src/cpp/client/xds_credentials.h',
        'src/src/cpp/server/csds/csds.cc',
        'src/src/cpp/server/xds_server_credentials.cc',
        'src/src/cpp/server/xds_server_credentials.cc',
        'src/src/cpp/server/xds_server_credentials.h',
        'src/src/core/ext/filters/rbac/rbac_filter.cc',
        'src/src/core/ext/filters/rbac/rbac_filter.h',
        'src/src/core/ext/filters/rbac/rbac_service_config_parser.cc',
        'src/src/core/ext/filters/rbac/rbac_service_config_parser.h',
        'src/src/core/lib/security/authorization/grpc_authorization_engine.cc',
        'src/src/core/lib/security/authorization/grpc_authorization_engine.h',
        'src/src/core/lib/security/authorization/matchers.cc',
        'src/src/core/lib/security/authorization/matchers.h',
        'src/src/core/lib/security/authorization/rbac_policy.cc',
        'src/src/core/lib/security/authorization/rbac_policy.h',
    ]
    return s in file_names

  # Add comments for some files.
  def get_commented_sources(sources):
    out_sources = []
    for s in sources:
      if s == 'src/src/core/lib/gpr/wrap_memcpy.cc':
        out_sources.append('# gRPC memcpy wrapping logic isn\'t useful here.')
        out_sources.append('# See https://crbug.com/661171')
        out_sources.append('# "{}",'.format(s))
      elif s == 'src/src/core/plugin_registry/grpc_plugin_registry.cc':
        out_sources.append('# Disabling some default plugins.')
        out_sources.append('# "{}",'.format(s))
        out_sources.append('"plugin_registry/grpc_plugin_registry.cc",')
      elif s == 'src/src/core/lib/matchers/matchers.cc':
        # matchers are disabled to reduce binary size
        out_sources.append('# "{}",'.format(s))
      elif s == 'src/src/core/lib/matchers/matchers.h':
        # matchers are disabled to reduce binary size
        out_sources.append('# "{}",'.format(s))
      elif is_xds_source(s):
        # xds is disabled to reduce binary size
        # We need to manually remove these sources because generated build
        # target info provided by upstream does not consider xds disabled
        out_sources.append('# "{}",'.format(s))
      else:
        out_sources.append('"{}",'.format(s))
    return out_sources

  # Get dependencies for a target.
  def get_deps_from_target(target_dict):
    deps = set()
    if target_dict.get("secure", False):
      deps.add("//third_party/boringssl")
    if target_dict.get("build", None) == "protoc":
      deps.add("//third_party/protobuf:protoc_lib")
    name = target_dict.get("name", None)
    if name in ("grpc++", "grpc++_codegen_lib"):
      deps.add("//third_party/protobuf:protobuf_lite")
    elif name in ("grpc", "grpc_unsecure"):
      deps.add("//third_party/zlib")
    add_absl = False
    add_boring_ssl = False
    for d in target_dict.get("deps", []):
      if d.startswith('libssl'):
        add_boring_ssl = True
      elif d.startswith('absl'):
        add_absl = True
      elif d.startswith(("//", ":")):
        deps.add(d)
      else:
        deps.add(":%s" % d)
    if add_absl:
      deps.add("//third_party/abseil-cpp:absl")
    if add_boring_ssl:
      deps.add("//third_party/boringssl",)
    return list(deps)

  # Get dependencies for a list of sources.
  def get_deps_from_sources(sources):
    deps = set()
    if needs_ares(sources):
      deps.add(":cares")
      deps.add(":address_sorting")
    return list(deps)

  def needs_ares(srcs):
    return any("/c_ares/" in f for f in srcs) if srcs else False

  def needs_address_sorting(sources):
    return needs_ares(sources) or any("address_sorting" in s for s in sources)

  def get_include_dirs(sources):
    dirs = []
    if needs_ares(sources):
      dirs = [":cares"]
    if needs_address_sorting(sources):
      dirs.append("src/third_party/address_sorting/include")
    return dirs

  def get_extra_stuff():
    extra_stuff = []
    extra_stuff.append('visibility = [ "./*" ]')
    extra_stuff.append('if (!grpc_use_static_linking) {')
    extra_stuff.append('  configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]')
    extra_stuff.append('  configs += [ "//build/config/gcc:symbol_visibility_default" ]')
    extra_stuff.append('}')
    return extra_stuff


  def strip_sources(sources):
    exceptions = [
      "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_balancer_addresses.h",
      "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_balancer_addresses.cc",
      "src/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc",
    ]

    return [f for f in sources
            if "ruby_generator" not in f
            and not (re.match("src/src/core/ext/filters/client_channel/lb_policy/.*/.*",f)
                and not f in exceptions)
            and not re.match("src/src/core/ext/filters/client_channel/resolver/xds/.*",f)
            and not re.match("src/src/core/ext/xds/.*",f)
            ]

  def adjust_srcs(sources):
    return ["src/" + f for f in sources]

  def get_sources(target):
    sources = (([] if not hasattr(target, "public_headers") else target.public_headers or []) +
            (target.headers or []) +
            (target.src or []))
    return adjust_srcs(sources)

  def in_main_lib(lib):
    main_libs = ("gpr", "grpc", "grpc++")
    return lib.name in main_libs

  def wanted_lib(lib):
    wanted_libs = ("grpc_plugin_support", "address_sorting", "upb")
    return lib.build in ("all", "protoc") and lib.get("name", "") in wanted_libs

  def wanted_binary(tgt):
    wanted_binaries = ("grpc_cpp_plugin",)
    return tgt.build == "protoc" and tgt.get("name", "") in wanted_binaries

  def only_on_host_toolchain(tgt):
    return tgt.get("name", "") in ("grpc_plugin_support", "grpc_cpp_plugin")

  def find_sources(path):
    sources = []
    for (root, ds, fs) in os.walk(path):
      for f in fs:
        ext = os.path.splitext(f)
        if (len(ext) > 1) and (ext[1] in ('.h', '.c')):
          sources.append(os.path.join(root, f))
    return ["src/" + s for s in sources]

  cares_sources = glob.glob('third_party/cares/cares/include/*.h') + \
    glob.glob('third_party/cares/cares/src/lib/*.c')
  # cares is only used in chromecast. Use chromium/src/third_party/cares in chromecast
  cares_sources = gn_sort([s.replace('third_party/cares/cares', '//third_party/cares')
                           for s in cares_sources])
  cares_sources = [s for s in cares_sources if s not in (
    '//third_party/cares/cares/src/lib/ahost.c',
    '//third_party/cares/cares/src/lib/adig.c',
    '//third_party/cares/cares/src/lib/acountry.c')]

  %>\
  <%doc>
  Body of GN file
  </%doc>\
  ${cc_main_library(libs)}
  % for lib in libs:
    % if wanted_lib(lib):
      % if only_on_host_toolchain(lib):
  # Only compile the plugin for the host architecture.
  if (current_toolchain == host_toolchain) {
    ${cc_library(lib, "  ", True)}
  }
      % else:
  ${cc_library(lib, "", False)}
      % endif
    % endif
  % endfor
  % for tgt in targets:
    % if wanted_binary(tgt):
      % if only_on_host_toolchain(tgt):
  # Only compile the plugin for the host architecture.
  if (current_toolchain == host_toolchain) {${cc_binary(tgt, "  ")}}
      % else:
        ${cc_binary(tgt, "")}
      % endif
    % endif
  % endfor
  <%doc>
  Template Functions
  </%doc>\
  <%def name="cc_main_library(libs)">\
  <%
    extra_configs = [':grpc_config_private']
    sources = []
    headers = []
    deps = []
    upb_sources = []
    for lib in libs:
      if lib.name == 'upb':
        upb_sources = get_sources(lib)
    for lib in libs:
      if in_main_lib(lib):
        if lib.src:
          sources += lib.src
        if lib.headers:
          headers += lib.headers
        if lib.public_headers:
          headers += lib.public_headers
        deps += get_deps_from_target(lib)
    headers = adjust_srcs(headers)
    headers = [f for f in headers if f not in upb_sources]
    headers = strip_sources(headers)
    sources = adjust_srcs(sources)
    sources = [f for f in sources if f not in upb_sources]
    sources = strip_sources(sources)
    (sources, repeated1, repeated2, repeated3, repeated4) = find_repeated(sources)
    deps = [d for d in deps if d not in (':gpr', ':grpc')]
    cc_lib_name = 'grpc++_cc'
    h_lib_name = 'grpc++_h'
    repeated_lib_name1 = 'grpc++_repeated1'
    repeated_lib_name2 = 'grpc++_repeated2'
    repeated_lib_name3 = 'grpc++_repeated3'
    repeated_lib_name4 = 'grpc++_repeated4'
    other_deps = deps[:]
    other_deps.append(":{}".format(h_lib_name))
    deps_so = [
      ':{}'.format(cc_lib_name),
      ':{}'.format(repeated_lib_name1),
      ':{}'.format(repeated_lib_name2),
      ':{}'.format(repeated_lib_name3),
      ':{}'.format(repeated_lib_name4),
    ]
    public_deps_so = [
      ':{}'.format(h_lib_name),
    ]
    extra_stuff = get_extra_stuff()
  %>\
  grpc_so("grpc++") {
    deps = [
  % for dep in deps_so:
      "${dep}",
  % endfor
    ]
    public_deps = [
  % for dep in public_deps_so:
      "${dep}",
  % endfor
    ]
  }
  # GN doesn't like .cc files with the same base name in the same target.
  # Moving them to another target.
  ${cc_library_internal(h_lib_name, '', headers, deps, extra_stuff, extra_configs)}

  ${cc_library_internal(cc_lib_name, '', sources, other_deps, extra_stuff, extra_configs)}

  ${cc_library_internal(repeated_lib_name1, '', repeated1, other_deps, extra_stuff, extra_configs)}
  ${cc_library_internal(repeated_lib_name2, '', repeated2, other_deps, extra_stuff, extra_configs)}
  ${cc_library_internal(repeated_lib_name3, '', repeated3, other_deps, extra_stuff, extra_configs)}
  ${cc_library_internal(repeated_lib_name4, '', repeated4, other_deps, extra_stuff, extra_configs)}
  </%def>\
  <%def name="cc_library(lib, indent, is_host)">\
  <%
      sources = get_sources(lib)
      sources = strip_sources(sources)
      repeated_lib_name1 = "{}_repeated1".format(lib.name)
      repeated_lib_name2 = "{}_repeated2".format(lib.name)
      repeated_lib_name3 = "{}_repeated3".format(lib.name)
      repeated_lib_name4 = "{}_repeated4".format(lib.name)
      (sources, repeated1, repeated2, repeated3, repeated4) = find_repeated(sources)
      extra_configs = [':grpc_config_private']
      extra_stuff = []
      target_type = 'source_set'
      if not is_host:
        extra_stuff = get_extra_stuff()
      deps = get_deps_from_target(lib)
      repeated_deps = deps[:]
      if repeated1:
        deps.append(":{}".format(repeated_lib_name1))
      if repeated2:
        deps.append(":{}".format(repeated_lib_name2))
      if repeated3:
        deps.append(":{}".format(repeated_lib_name3))
      if repeated4:
        deps.append(":{}".format(repeated_lib_name4))
    %>\
  ${cc_library_internal(lib.name, indent, sources, deps, extra_stuff, extra_configs)}
    % if repeated1:

  # GN doesn't like .cc files with the same base name in the same target.
  # Moving them to another target.
  ${cc_library_internal(repeated_lib_name1, indent, repeated1, repeated_deps, extra_stuff, extra_configs)}
    % endif
    % if repeated2:

  # There are some .cc files that are in multiple places. GN doesn't like
  # that. Moving them to another target.
  ${cc_library_internal(repeated_lib_name2, indent, repeated2, repeated_deps, extra_stuff, extra_configs)}
    % endif
    % if repeated3:

  # There are some .cc files that are in multiple places. GN doesn't like
  # that. Moving them to another target.
  ${cc_library_internal(repeated_lib_name3, indent, repeated3, repeated_deps, extra_stuff, extra_configs)}
    % endif
    % if repeated4:

  # There are some .cc files that are in multiple places. GN doesn't like
  # that. Moving them to another target.
  ${cc_library_internal(repeated_lib_name4, indent, repeated4, repeated_deps, extra_stuff, extra_configs)}
    % endif

  </%def>\
  <%def name="cc_library_internal(name, indent, sources, lib_deps, extra_stuff, extra_configs)">\
  <%
      include_dirs = get_include_dirs(sources)
      lib_deps += get_deps_from_sources(sources)
      lib_deps = gn_sort(lib_deps)
      sources = gn_sort(sources)
      sources = get_commented_sources(sources)
    %>\
  ${indent}source_set("${name}") {
    % if sources:
  ${indent}  sources = [
      % for src in sources:
  ${indent}    ${src}
      % endfor
  ${indent}  ]
    % endif
    % if lib_deps:

  ${indent}  deps = [
      % for dep in lib_deps:
  ${indent}    "${dep}",
      % endfor
  ${indent}  ]
    % endif
  ${indent}  public_configs = [
  ${indent}    ":grpc_config",
  ${indent}  ]
    % if extra_configs:
  ${indent}  configs += [
        % for config in extra_configs:
  ${indent}    "${config}",
        % endfor
  ${indent}  ]
    % endif
    % if include_dirs:
  ${indent}  include_dirs = [
      % for d in include_dirs:
  ${indent}    "${d}",
      % endfor
  ${indent}  ]
    % endif
    % if extra_stuff:
      % for e in extra_stuff:
  ${indent}  ${e}
      % endfor
    % endif
  ${indent}}\
  </%def>
  <%def name="cc_binary(tgt, indent)">\
  <%
  sources = ["src/"+s for s in tgt.src]
  sources = gn_sort(sources)
  deps = get_deps_from_target(tgt) + get_deps_from_sources(sources)
  deps = gn_sort(deps)
  %>
  ${indent}executable("${tgt.name}") {
  ${indent}  sources = [
  % for src in sources:
  ${indent}    "${src}",
  % endfor
  ${indent}  ]
  ${indent}  deps = [
  % for dep in deps:
  ${indent}    "${dep}",
  % endfor
  ${indent}  ]
  ${indent}  configs += [
  ${indent}    "//third_party/protobuf:protobuf_config",
  ${indent}  ]
  ${indent}  public_configs = [ ":grpc_config" ]
  ${indent}}
  </%def><%!
  %>\
  <%doc>
  Manual targets
  </%doc>\
  config("cares_config") {
    cflags = [
      "-Wno-macro-redefined",
      "-Wno-unused-variable",
    ]
  }

  source_set("cares") {
    sources = [
      "src/third_party/cares/ares_build.h",
    ]

    if (enable_grpc_ares) {
      include_dirs = [ "//third_party/cares/include" ]
      sources += [
  % for src in cares_sources:
        "${src}",
  % endfor
      ]
    }

    if (is_android) {
      sources += [ "src/third_party/cares/config_android/ares_config.h" ]
      configs += [ ":cares_config" ]
    } else if (is_fuchsia) {
      sources += [ "third_party/cares/config_fuchsia/ares_config.h" ]
    } else {
      sources += [ "src/third_party/cares/config_linux/ares_config.h" ]
    }
    deps = [
      "//third_party/boringssl",
    ]

    public_configs = [ ":grpc_config" ]
  }

  group("fuzzers") {}

  if (is_android) {
    # gRPC BinderTransport's Java API and internal code, which cannot be handled by GN's source_set
    # and we need manually create a android_library target for them.
    android_library("binder_java_helper") {
      sources = [
        "src/src/core/ext/transport/binder/java/io/grpc/binder/cpp/GrpcBinderConnection.java",
        "src/src/core/ext/transport/binder/java/io/grpc/binder/cpp/GrpcCppServerBuilder.java",
        "src/src/core/ext/transport/binder/java/io/grpc/binder/cpp/NativeConnectionHelper.java",
      ]
    }
  }