# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
if (is_android) {
  import("//build/config/android/rules.gni")
}

# Includes default args like 'enable_js_protobuf'.
import("proto_library.gni")
import("proto_sources.gni")
import("proto_wkt.gni")
if (enable_js_protobuf) {
  import("//third_party/closure_compiler/compile_js.gni")
}

# Chromium uses two distinct protobuf targets:
# 1. `protobuf_lite`: The lightweight runtime used by production Chrome/Blink
#     code.
# 2. `protobuf_full`: The heavy runtime used by tests, fuzzers, and build tools
#    (`protoc`).
#
# In non-component builds, these are built as two static_libraries and full
# depends on both. In component builds, a single shared library is used for
# both, because they can't be broken into separate shared libraries. See
# https://crbug.com/40229487 for further background.

# This group allows `gn suggest` to suggest `:absl` instead of
# `//third_party/abseil-cpp:absl`
group("absl") {
  public_deps = [ "${protobuf_abseil_dir}:absl" ]
  visibility = [ ":*" ]
}

config("protobuf_config") {
  include_dirs = [
    "src",
    "third_party/utf8_range",
  ]
  defines = [
    "GOOGLE_PROTOBUF_NO_RTTI",
    "PROTOBUF_INTERNAL_BOUNDS_CHECK_MODE_ABORT",
  ]
  if (!is_win) {
    defines += [ "HAVE_PTHREAD" ]
  }
  if (is_component_build) {
    defines += [ "PROTOBUF_USE_DLLS" ]
  }
}

config("protobuf_config_internal") {
  include_dirs = [ "./third_party/utf8_range" ]
  visibility = [ ":*" ]

  if (is_clang) {
    cflags = [
      # For src/google/protobuf/map.cc:
      "-Wno-deprecated-this-capture",

      # For src/google/protobuf/generated_message_reflection.cc
      "-Wno-unused-lambda-capture",
    ]
  }
}

# This config should be applied to targets using generated code from the proto
# compiler. It sets up the include directories properly.
config("using_proto") {
  include_dirs = [
    "src",
    "third_party/utf8_range",
  ]
}

# Configs to remove from the main protobuf libraries (lite and full)
#
# Remove coverage and Sanitizers other than ASan for a performance boost when
# fuzzing. ASan can't be removed here because of a bug preventing unsanitized
# code from using libc++, which protobuf_full uses.
protobuf_library_configs_to_remove =
    [ "//build/config/compiler:chromium_code" ] + not_fuzzed_remove_configs

if (is_win) {
  protobuf_library_configs_to_remove += [ "//build/config/win:lean_and_mean" ]
}

# Configs to add to the main protobuf libraries (lite and full)
protobuf_library_configs_to_add = [
                                    "//build/config/compiler:no_chromium_code",
                                    ":protobuf_config_internal",
                                  ] + not_fuzzed_needs_asan_add_configs

# Build protobuf with full optimizations so Clang can optimize the
# initializer out. See 0029-make-initializers-optimizable.patch.
if (!is_debug && is_android) {
  protobuf_library_configs_to_remove +=
      [ "//build/config/compiler:default_optimization" ]
  protobuf_library_configs_to_add += [ "//build/config/compiler:optimize_max" ]
}

source_set("protobuf_headers_source_set") {
  visibility = [ ":*" ]
  public = protobuf_headers
  public_deps = [
    ":absl",
    ":utf8_range",
  ]
  public_configs = [ ":protobuf_config" ]
}

if (is_component_build) {
  component("protobuf_full_and_lite_library") {
    visibility = [
      ":protobuf_full",
      ":protobuf_lite",
    ]
    sources = protobuf_lite_sources + protobuf_sources
    deps = [ ":utf8_range" ]
    public_deps = [ ":protobuf_headers_source_set" ]

    configs -= protobuf_library_configs_to_remove
    configs += protobuf_library_configs_to_add

    defines = [ "LIBPROTOBUF_EXPORTS" ]
  }
} else {
  static_library("protobuf_lite_library") {
    visibility = [
      ":protobuf_full_library",
      ":protobuf_lite",
    ]
    sources = protobuf_lite_sources
    deps = [ ":utf8_range" ]
    public_deps = [ ":protobuf_headers_source_set" ]

    configs -= protobuf_library_configs_to_remove
    configs += protobuf_library_configs_to_add
  }

  static_library("protobuf_full_library") {
    visibility = [ ":protobuf_full" ]
    sources = protobuf_sources
    deps = [ ":utf8_range" ]
    public_deps = [
      ":protobuf_headers_source_set",
      ":protobuf_lite_library",
    ]

    configs -= protobuf_library_configs_to_remove
    configs += protobuf_library_configs_to_add
  }
}

group("protobuf_lite") {
  if (is_component_build) {
    public_deps = [ ":protobuf_full_and_lite_library" ]
  } else {
    public_deps = [ ":protobuf_lite_library" ]
  }
}

# This is the full, heavy protobuf lib that's needed for c++ .protos that don't
# specify the LITE_RUNTIME option. The protocol compiler itself (protoc) falls
# into that category. Do not use in Chrome code.
group("protobuf_full") {
  if (defined(build_with_chromium) && build_with_chromium) {
    # Prevent people from depending on this outside our file.
    visibility = [
      ":*",

      # Used for testing protobuf generation.
      "//base/test:proto_test_support",

      # requires descriptors & reflection; testonly.
      "//third_party/libprotobuf-mutator:*",

      # Chromecast requires descriptors and reflection.
      "//chromecast/*",

      # libassistant requires descriptors and reflection for testing.
      "//libassistant/*",

      # Perfetto uses the full library for testing.
      "//third_party/perfetto/gn:protobuf_full",

      # Some tests inside ChromeOS need reflection to parse golden files.
      # Not included in production code.
      "//chrome/browser/ash/child_accounts/time_limit_consistency_test:usage_time_limit_unittests",

      # The protobuf-based SQLite and GPU fuzzers need protobuf_full and are not
      # included in Chrome.
      "//gpu:gl_lpm_fuzzer_proto",
      "//gpu:gl_lpm_fuzzer_proto_gen",
      "//gpu:gl_lpm_shader_to_string_unittest",
      "//third_party/sqlite:sqlite3_lpm_corpus_gen",

      # The protobuf-based Mojo LPM fuzzer needs protobuf_full and is not
      # included in Chrome.
      "//mojo/public/tools/fuzzers:mojolpm",

      # The root store tool is not part of Chrome itself, and needs to parse
      # human-readable protobufs. Protobuf is stored in //net/cert however as
      # browser needs to be able to parse serialized protobuf (which is exposed
      # as a separate lite BUILD rule).
      "//net/cert:root_store_proto_full",

      # The spirv-fuzz fuzzer tool needs protobuf_full and is not included in
      # Chrome.
      "//third_party/spirv-tools/src:spirv-fuzz",
      "//third_party/spirv-tools/src:spvtools_fuzz",
      "//third_party/spirv-tools/src:spvtools_fuzz_proto",

      # Some fuzzers for tint need protobuf_full and are not included in Chrome.
      "//third_party/dawn/src/tint/*",

      # Dawn LPM Fuzzers
      "//third_party/dawn/src/dawn/fuzzers:dawn_lpm_proto",
      "//third_party/dawn/src/dawn/fuzzers:dawn_lpm_proto_gen",

      # The Cast Core gRPC generator tool.
      "//third_party/cast_core/public/src/build/chromium:cast_core_grpc_generator",

      # The proto_extras plugin uses a custom option, which must be defined in a
      # full protobuf. This is only used in the plugin binary.
      "//components/proto_extras:proto_extras_options_proto",

      # Sync requires descriptors for testing.
      "//components/sync:sync_full_proto_unittests",
      "//components/sync/protocol:full_proto",

      # third_party/federated_compute requires it for testing.
      "//third_party/federated_compute:federated_compute_tests",
    ]
  }

  if (is_component_build) {
    public_deps = [ ":protobuf_full_and_lite_library" ]
  } else {
    public_deps = [ ":protobuf_full_library" ]
  }
}

# Only compile the compiler for the host architecture.
if (current_toolchain == host_toolchain) {
  # Code for compiling bindings for individual languages are pulled into
  # separate build targets for two reasons:
  # 1. To avoid naming collisions between files in the same source set.
  # 2. Because Chromium doesn't need a lot of the languages.

  # Note we have to separate the 3 Java directories (java/, java/full/ and
  # java/lite/) into 3 different GN targets because they contain .cc files with
  # identical names (e.g. message.cc), and source_set() doesn't support multiple
  # .cc files with the same basename. We use a single target for the headers as
  # these directories tend to cross-#include each other which makes things
  # challenging for GN header dependency checks.
  source_set("protoc_java_all_headers") {
    sources = protoc_java_all_headers
    configs += [ ":libprotoc_config" ]
    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [ "//build/config/compiler:no_chromium_code" ]

    configs += [ ":protobuf_config_internal" ]

    public_deps = [ ":protobuf_full" ]
  }
  source_set("protoc_java") {
    sources = protoc_java_sources
    configs += [ ":libprotoc_config" ]
    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [ "//build/config/compiler:no_chromium_code" ]

    configs += [ ":protobuf_config_internal" ]

    public_deps = [
      ":protobuf_full",
      ":protoc_java_all_headers",
    ]
  }
  source_set("protoc_java_full") {
    sources = protoc_java_full_sources
    configs += [ ":libprotoc_config" ]
    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [ "//build/config/compiler:no_chromium_code" ]

    configs += [ ":protobuf_config_internal" ]

    public_deps = [
      ":protobuf_full",
      ":protoc_java_all_headers",
    ]
  }
  source_set("protoc_java_lite") {
    sources = protoc_java_lite_sources
    configs += [ ":libprotoc_config" ]
    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [ "//build/config/compiler:no_chromium_code" ]

    configs += [ ":protobuf_config_internal" ]

    public_deps = [
      ":protobuf_full",
      ":protoc_java_all_headers",
    ]
  }

  source_set("protoc_cpp") {
    sources = protoc_cpp_sources + protoc_cpp_headers
    configs += [ ":libprotoc_config" ]
    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [ "//build/config/compiler:no_chromium_code" ]

    configs += [ ":protobuf_config_internal" ]

    public_deps = [ ":protobuf_full" ]
  }

  source_set("protoc_python") {
    sources = protoc_python_sources + protoc_python_headers
    configs += [ ":libprotoc_config" ]
    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [ "//build/config/compiler:no_chromium_code" ]

    configs += [ ":protobuf_config_internal" ]

    public_deps = [ ":protobuf_full" ]
  }

  # In component builds, protoc targets depending on `protobuf_lite` inherit
  # PROTOBUF_USE_DLLS. This causes PROTOC_EXPORT to resolve to dllimport on
  # Windows, which leads to compilation errors (`-Winconsistent-dllimport`)
  # since the dllimports are for symbols that are implemented locally. Defining
  # LIBPROTOC_EXPORTS forces PROTOC_EXPORT to resolve to dllexport to fix the
  # issue.
  config("libprotoc_config") {
    defines = [ "LIBPROTOC_EXPORTS" ]
  }

  # protoc generates language-specific bindings from protos.
  static_library("protoc_lib") {
    sources = protoc_sources + protoc_headers

    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [ "//build/config/compiler:no_chromium_code" ]
    configs += [ ":protobuf_config_internal" ]

    public_configs = [ ":protobuf_config" ]

    # In component builds, targets depending on protoc_lib inherit
    # PROTOBUF_USE_DLLS, which makes them dllimport protoc symbols. However,
    # protoc_lib is a static_library and thus part of these targets. This
    # causes link failures on Windows when trying to dllimport symbols that
    # are defined locally.
    #
    # Adding this config to public_configs forces these targets to dllexport
    # protoc symbols instead.
    public_configs += [ ":libprotoc_config" ]

    public_deps = [ ":protobuf_full" ]
    deps = [
      ":protoc_cpp",
      ":protoc_java",
      ":protoc_java_all_headers",
      ":protoc_java_full",
      ":protoc_java_lite",
      ":protoc_python",
    ]
    allow_circular_includes_from = [
      ":protoc_java",
      ":protoc_java_all_headers",
      ":protoc_java_full",
      ":protoc_java_lite",
      ":protoc_cpp",
      ":protoc_python",
    ]
  }

  executable("protoc") {
    sources = [ "src/google/protobuf/compiler/main.cc" ]

    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [ "//build/config/compiler:no_chromium_code" ]
    configs += [ ":protobuf_config_internal" ]

    deps = [
      ":protoc_cpp",
      ":protoc_java",
      ":protoc_java_full",
      ":protoc_java_lite",
      ":protoc_lib",
      ":protoc_python",
    ]
    if (enable_js_protobuf) {
      deps += [ "//third_party/protobuf-javascript:protoc-gen-js" ]
    }
  }
}

source_set("utf8_range") {
  sources = [
    "third_party/utf8_range/utf8_range.c",
    "third_party/utf8_range/utf8_range.h",
    "third_party/utf8_range/utf8_validity.h",
  ]

  configs -= [ "//build/config/compiler:chromium_code" ]
  configs += [ "//build/config/compiler:no_chromium_code" ]
  configs += [ ":protobuf_config_internal" ]

  deps = [ ":absl" ]
}

copy("copy_google_protobuf") {
  # See README.chromium for how this file is generated.
  sources = pyproto_sources + [ "python/google/protobuf/descriptor_pb2.py" ]
  outputs = [ "$proto_python_root/google/protobuf/{{source_file_part}}" ]
}

copy("copy_google_protobuf_internal") {
  # See README.chromium for how this file is generated.
  sources = pyproto_internal_sources +
            [ "python/google/protobuf/internal/python_edition_defaults.py" ]
  outputs =
      [ "$proto_python_root/google/protobuf/internal/{{source_file_part}}" ]
}

# Build time dependency for action rules.
group("py_proto") {
  public_deps = [
    ":copy_google_protobuf",
    ":copy_google_protobuf_internal",
  ]
}

# Note: This exists for Cronet in AOSP where we can't import prebuilts. We
# import the raw sources and compile them using a specialized pipeline. See
# crbug.com/419480317.
if (is_android && is_cronet_for_aosp_build) {
  android_library("proto_runtime_lite_java") {
    sources = javaproto_sources
  }
}

# Runtime dependency if the target needs the python scripts.
group("py_proto_runtime") {
  deps = [ ":py_proto" ]

  # Targets that depend on this should depend on the copied data files.
  data = get_target_outputs(":copy_google_protobuf")
  data += get_target_outputs(":copy_google_protobuf_internal")
}

# JS protobuf library.
if (enable_js_protobuf) {
  js_library("js_proto") {
    sources = [
      "//third_party/google-closure-library/closure/goog/array/array.js",
      "//third_party/google-closure-library/closure/goog/asserts/asserts.js",
      "//third_party/google-closure-library/closure/goog/asserts/dom.js",
      "//third_party/google-closure-library/closure/goog/async/throwexception.js",
      "//third_party/google-closure-library/closure/goog/base.js",
      "//third_party/google-closure-library/closure/goog/crypt/base64.js",
      "//third_party/google-closure-library/closure/goog/crypt/crypt.js",
      "//third_party/google-closure-library/closure/goog/debug/error.js",
      "//third_party/google-closure-library/closure/goog/dom/asserts.js",
      "//third_party/google-closure-library/closure/goog/dom/browserfeature.js",
      "//third_party/google-closure-library/closure/goog/dom/dom.js",
      "//third_party/google-closure-library/closure/goog/dom/element.js",
      "//third_party/google-closure-library/closure/goog/dom/htmlelement.js",
      "//third_party/google-closure-library/closure/goog/dom/nodetype.js",
      "//third_party/google-closure-library/closure/goog/dom/safe.js",
      "//third_party/google-closure-library/closure/goog/dom/tagname.js",
      "//third_party/google-closure-library/closure/goog/dom/tags.js",
      "//third_party/google-closure-library/closure/goog/flags/flags.js",
      "//third_party/google-closure-library/closure/goog/fs/blob.js",
      "//third_party/google-closure-library/closure/goog/fs/url.js",
      "//third_party/google-closure-library/closure/goog/functions/functions.js",
      "//third_party/google-closure-library/closure/goog/goog.js",
      "//third_party/google-closure-library/closure/goog/html/safehtml.js",
      "//third_party/google-closure-library/closure/goog/html/safescript.js",
      "//third_party/google-closure-library/closure/goog/html/safestyle.js",
      "//third_party/google-closure-library/closure/goog/html/safestylesheet.js",
      "//third_party/google-closure-library/closure/goog/html/safeurl.js",
      "//third_party/google-closure-library/closure/goog/html/trustedresourceurl.js",
      "//third_party/google-closure-library/closure/goog/html/trustedtypes.js",
      "//third_party/google-closure-library/closure/goog/html/uncheckedconversions.js",
      "//third_party/google-closure-library/closure/goog/i18n/bidi.js",
      "//third_party/google-closure-library/closure/goog/labs/useragent/browser.js",
      "//third_party/google-closure-library/closure/goog/labs/useragent/engine.js",
      "//third_party/google-closure-library/closure/goog/labs/useragent/highentropy/highentropydata.js",
      "//third_party/google-closure-library/closure/goog/labs/useragent/highentropy/highentropyvalue.js",
      "//third_party/google-closure-library/closure/goog/labs/useragent/platform.js",
      "//third_party/google-closure-library/closure/goog/labs/useragent/useragent.js",
      "//third_party/google-closure-library/closure/goog/labs/useragent/util.js",
      "//third_party/google-closure-library/closure/goog/math/coordinate.js",
      "//third_party/google-closure-library/closure/goog/math/math.js",
      "//third_party/google-closure-library/closure/goog/math/size.js",
      "//third_party/google-closure-library/closure/goog/memoize/memoize.js",
      "//third_party/google-closure-library/closure/goog/object/object.js",
      "//third_party/google-closure-library/closure/goog/reflect/reflect.js",
      "//third_party/google-closure-library/closure/goog/string/const.js",
      "//third_party/google-closure-library/closure/goog/string/internal.js",
      "//third_party/google-closure-library/closure/goog/string/string.js",
      "//third_party/google-closure-library/closure/goog/string/typedstring.js",
      "//third_party/google-closure-library/closure/goog/useragent/product.js",
      "//third_party/google-closure-library/closure/goog/useragent/useragent.js",
      "//third_party/protobuf-javascript/src/asserts.js",
      "//third_party/protobuf-javascript/src/binary/any_field_type.js",
      "//third_party/protobuf-javascript/src/binary/arith.js",
      "//third_party/protobuf-javascript/src/binary/binary_constants.js",
      "//third_party/protobuf-javascript/src/binary/bytesource.js",
      "//third_party/protobuf-javascript/src/binary/decoder.js",
      "//third_party/protobuf-javascript/src/binary/decoder_alias.js",
      "//third_party/protobuf-javascript/src/binary/encoder.js",
      "//third_party/protobuf-javascript/src/binary/encoder_alias.js",
      "//third_party/protobuf-javascript/src/binary/errors.js",
      "//third_party/protobuf-javascript/src/binary/internal_buffer.js",
      "//third_party/protobuf-javascript/src/binary/reader.js",
      "//third_party/protobuf-javascript/src/binary/reader_alias.js",
      "//third_party/protobuf-javascript/src/binary/repeated_field_type.js",
      "//third_party/protobuf-javascript/src/binary/scalar_field_type.js",
      "//third_party/protobuf-javascript/src/binary/utf8.js",
      "//third_party/protobuf-javascript/src/binary/utils.js",
      "//third_party/protobuf-javascript/src/binary/writer.js",
      "//third_party/protobuf-javascript/src/binary/writer_alias.js",
      "//third_party/protobuf-javascript/src/bytestring.js",
      "//third_party/protobuf-javascript/src/internal_bytes.js",
      "//third_party/protobuf-javascript/src/internal_options.js",
      "//third_party/protobuf-javascript/src/internal_public.js",
      "//third_party/protobuf-javascript/src/map.js",
      "//third_party/protobuf-javascript/src/message.js",
      "//third_party/protobuf-javascript/src/unsafe_bytestring.js",
    ]
  }
}

source_set("time_util") {
  public = [ "src/google/protobuf/util/time_util.h" ]
  sources = [ "src/google/protobuf/util/time_util.cc" ]
  public_deps = [
    ":duration_proto",
    ":timestamp_proto",
  ]
  deps = [ ":protobuf_lite" ]
}

source_set("io_gzip_stream") {
  public = [ "src/google/protobuf/io/gzip_stream.h" ]
  sources = [ "src/google/protobuf/io/gzip_stream.cc" ]

  public_deps = [
    ":protobuf_lite",
    "//third_party/zlib",
  ]

  defines = [ "HAVE_ZLIB" ]
}

proto_wkt("any_proto") {
  sources = [ "src/google/protobuf/any.proto" ]

  full_target_name = "any_full_proto"
  full_target_visibility = [
    # libprotobuf-mutator uses the full version of any.proto internally.
    "//third_party/libprotobuf-mutator",

    # only used in fuzzing builds.
    "//net/third_party/quiche:blind_sign_auth_proto",
    "//net/third_party/quiche:blind_sign_auth_test_support",
  ]
}

proto_wkt("duration_proto") {
  sources = [ "src/google/protobuf/duration.proto" ]
}

proto_wkt("struct_proto") {
  sources = [ "src/google/protobuf/struct.proto" ]
}

proto_wkt("timestamp_proto") {
  sources = [ "src/google/protobuf/timestamp.proto" ]
}
