# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("perfetto.gni")
import("perfetto_android_sdk.gni")
import("perfetto_python.gni")
import("pkg_config.gni")
import("proto_library.gni")

if (perfetto_root_path == "//") {
  import("//gn/standalone/sanitizers/vars.gni")
} else {
  import("//build/config/sanitizers/sanitizers.gni")
}

# Genereates a header files that contains a macro definition for each build flag
# that is required by the codebase. This is to avoid sprinkling cflags all over
# the places, which is very fragile especially for our codebase that needs to
# deal with several build systems.
# The way this works is the following:
# - This rule generates a header that contains a bunch of lines like:
#   #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ANDROID_BUILD()
# - The generated header is included by base/build_config.h
# - Source files in the codebase #include base/build_config and use the
#   pattern #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
buildflag_gen_dir_ = "$root_gen_dir/$perfetto_root_path/build_config"
action("gen_buildflags") {
  script = "write_buildflag_header.py"
  gen_header_path = "$buildflag_gen_dir_/perfetto_build_flags.h"

  perfetto_component_build = false
  if (defined(is_component_build) && is_component_build) {
    perfetto_component_build = true
  }
  perfetto_force_dlog_on = perfetto_force_dlog == "on"
  perfetto_force_dlog_off = perfetto_force_dlog == "off"

  perfetto_force_dcheck_on = perfetto_force_dcheck == "on"
  perfetto_force_dcheck_off = perfetto_force_dcheck == "off"

  # We can't just use (is_linux || is_android) in perfetto.gni because that
  # doesn't work in Android Mac host builds. We lose the GN notion of OS once
  # we run the tools/gen_xxx generators.
  #
  # Currently, QNX has the OS_LINUX flag enabled to remain as compatible as
  # possible. Watchdog is an exception where it uses Linux-specific system
  # calls. Therefore we skip enabling the watchdog in QNX.
  if (enable_perfetto_watchdog) {
    perfetto_watchdog =
        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() || " +
        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX_BUT_NOT_QNX()"
  } else {
    perfetto_watchdog = "0"
  }

  # We need local symbolization to run diff tests in chrome.
  if (enable_perfetto_trace_processor &&
      (perfetto_build_standalone || build_with_chromium) &&
      !perfetto_build_with_android) {
    perfetto_local_symbolizer =
        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || " +
        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FREEBSD() || " +
        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||" +
        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN()"
  } else {
    perfetto_local_symbolizer = "0"
  }
  response_file_contents = [
    "--flags",  # Keep this marker first.
    "PERFETTO_ANDROID_BUILD=$perfetto_build_with_android",
    "PERFETTO_CHROMIUM_BUILD=$build_with_chromium",
    "PERFETTO_STANDALONE_BUILD=$perfetto_build_standalone",
    "PERFETTO_START_DAEMONS=$start_daemons_for_testing",
    "PERFETTO_IPC=$enable_perfetto_ipc",
    "PERFETTO_WATCHDOG=$perfetto_watchdog",
    "PERFETTO_COMPONENT_BUILD=$perfetto_component_build",
    "PERFETTO_ENABLE_ETM_IMPORTER=$enable_perfetto_etm_importer",
    "PERFETTO_LLVM_SYMBOLIZER=$enable_perfetto_llvm_symbolizer",
    "PERFETTO_FORCE_DLOG_ON=$perfetto_force_dlog_on",
    "PERFETTO_FORCE_DLOG_OFF=$perfetto_force_dlog_off",
    "PERFETTO_FORCE_DCHECK_ON=$perfetto_force_dcheck_on",
    "PERFETTO_FORCE_DCHECK_OFF=$perfetto_force_dcheck_off",
    "PERFETTO_HTTP_ADDITIONAL_CORS_ORIGINS=\"$perfetto_http_additional_cors_origins\"",
    "PERFETTO_VERBOSE_LOGS=$perfetto_verbose_logs_enabled",
    "PERFETTO_VERSION_GEN=$enable_perfetto_version_gen",
    "PERFETTO_TP_PERCENTILE=$enable_perfetto_trace_processor_percentile",
    "PERFETTO_TP_LINENOISE=$enable_perfetto_trace_processor_linenoise",
    "PERFETTO_TP_HTTPD=$enable_perfetto_trace_processor_httpd",
    "PERFETTO_TP_INSTRUMENTS=$enable_perfetto_trace_processor_mac_instruments",
    "PERFETTO_LOCAL_SYMBOLIZER=$perfetto_local_symbolizer",
    "PERFETTO_ZLIB=$enable_perfetto_zlib",
    "PERFETTO_TRACED_PERF=$enable_perfetto_traced_perf",
    "PERFETTO_HEAPPROFD=$enable_perfetto_heapprofd",
    "PERFETTO_STDERR_CRASH_DUMP=$enable_perfetto_stderr_crash_dump",
    "PERFETTO_X64_CPU_OPT=$enable_perfetto_x64_cpu_opt",
    "PERFETTO_LLVM_DEMANGLE=$enable_perfetto_llvm_demangle",
    "PERFETTO_SYSTEM_CONSUMER=$enable_perfetto_system_consumer",
    "PERFETTO_THREAD_SAFETY_ANNOTATIONS=$perfetto_thread_safety_annotations",
    "PERFETTO_ENABLE_WINSCOPE=$enable_perfetto_winscope",
    "PERFETTO_ENABLE_RT_MUTEX=$enable_perfetto_rt_mutex",
    "PERFETTO_ENABLE_LOCKFREE_TASKRUNNER=$enable_perfetto_lockfree_taskrunner",
    "PERFETTO_ENABLE_SOCK_INOTIFY=$enable_perfetto_sock_inotify",
  ]

  rel_out_path = rebase_path(gen_header_path, "$root_build_dir")
  args = [
    "--out",
    rel_out_path,
    "--rsp",
    "{{response_file_name}}",
  ]

  outputs = [ gen_header_path ]
}

# All targets should depend on this target to inherit the right flags and
# include directories.
group("default_deps") {
  visibility = [ "../*" ]  # Prevent chromium targets from depending on this
                           # (breaks component).
  public_configs = [ ":default_config" ]
  deps = [ ":gen_buildflags" ]
  if (perfetto_build_standalone) {
    public_deps = [
      "//gn/standalone:check_build_deps",
      "//gn/standalone/libc++:deps",
      "//gn/standalone/sanitizers:deps",
    ]
    if (is_android) {
      public_deps += [ "//gn/standalone:check_build_deps_android" ]
    }
  }
}

# The config that all targets in the perfetto codebase inherit by virtue of
# having explicit deps on //gn:default_deps. This config is NOT propagated up to
# embedders that depend on perfetto (e.g. chrome). :public_config (see below) is
# used for that.
config("default_config") {
  visibility = [ "../*" ]  # Prevent chromium targets from depending on this
                           # (breaks component).
  configs = [ ":public_config" ]
  defines = [ "PERFETTO_IMPLEMENTATION" ]
  include_dirs = [
    "..",
    "../src/profiling/memory/include",
  ]

  if (build_with_chromium && is_android) {
    # Included for __android_log_print
    libs = [ "log" ]
  }
}

# This config is propagated to embedders via libperfetto. It's also included in
# the default_config above.
config("public_config") {
  include_dirs = [
    "../include",

    # For perfetto_build_flags.h
    buildflag_gen_dir_,

    # For generated files (proto libraries etc). We add the directory here
    # because we stop propagation of the configs for individual proto libraries
    # to avoid duplicate include directory command line flags in compiler
    # invocations, see proto_library.gni, crbug.com/1043279, crbug.com/gn/142.
    "$root_gen_dir/$perfetto_root_path",
  ]
}

config("asan_instrumentation") {
  if (use_sanitizer_configs_without_instrumentation) {
    defines = [ "ADDRESS_SANITIZER_WITHOUT_INSTRUMENTATION" ]
  }
}

if (perfetto_root_path != "//") {
  config("gtest_and_gmock_embedder_config") {
    include_dirs = [
      "//testing/gtest/include",
      "//testing/gmock/include",
    ]
  }
}

group("gtest_and_gmock") {
  testonly = true

  if (perfetto_root_path == "//") {
    public_deps = [
      "//buildtools:gmock",
      "//buildtools:gtest",
    ]
  } else {
    public_configs = [ ":gtest_and_gmock_embedder_config" ]
    public_deps = [
      "//testing/gmock",
      "//testing/gtest",
    ]
  }
}

group("gtest_main") {
  testonly = true

  if (perfetto_root_path == "//") {
    public_deps = [ "//buildtools:gtest_main" ]
  } else if (build_with_chromium) {
    public_deps = [ "//base/test:run_all_unittests" ]
  } else {
    public_deps = [ "//testing/gtest:gtest_main" ]
  }
}

# Full protobuf is just for host tools .No binary shipped on device should
# depend on this.
protobuf_full_deps_allowlist = [
  "../buildtools/grpc:*",
  "../src/ipc/protoc_plugin:*",
  "../src/tools:tracing_proto_extensions_lib",
  "../src/protozero/protoc_plugin:*",
  "../src/protozero:multifile_error_collector",
  "../src/protozero/filtering:filter_util",
  "../src/trace_processor:trace_processor_shell_lib",
  "../tools/*",
  "../src/tools/*",
  "../contrib/rust-sdk/tools/*",
]

group("protoc") {
  public_deps = [ "${perfetto_protobuf_target_prefix}:protoc($host_toolchain)" ]
}

config("system_protoc") {
  libs = [ "protoc" ]  # This will link against libprotoc.so
}

# pkg_deps selects the appropriate pkg-config based on current_toolchain and
# this is a no-op if |perfetto_use_pkgconfig| is false.
pkg_config("pkgconfig_protobuf") {
  pkg_deps = [ "protobuf" ]
}

config("system_protobuf") {
  if (perfetto_use_pkgconfig) {
    configs = [ ":pkgconfig_protobuf" ]
  } else {
    # Fallback if pkg-config isn't enabled.
    libs = [ "protobuf" ]  # This will link against libprotobuf.so
  }
}

# protoc compiler library, it's used for building protoc plugins.
group("protoc_lib") {
  visibility = protobuf_full_deps_allowlist
  if (current_toolchain == host_toolchain) {
    if (perfetto_use_system_protobuf) {
      public_configs = [
        ":system_protobuf",
        ":system_protoc",
        ":protobuf_gen_config",
      ]
    } else {
      public_deps = [ "${perfetto_protobuf_target_prefix}:protoc_lib" ]
    }
  }
}

group("protobuf_full") {
  visibility = protobuf_full_deps_allowlist
  if (perfetto_use_system_protobuf) {
    public_configs = [ ":system_protobuf" ]
  } else {
    public_deps = [ "${perfetto_protobuf_target_prefix}:protobuf_full" ]
  }
}

group("protobuf_lite") {
  if (perfetto_use_system_protobuf) {
    public_configs = [ ":system_protobuf" ]
  } else {
    public_deps = [ "${perfetto_protobuf_target_prefix}:protobuf_lite" ]
  }
}

# This config is applied to the .pb.{cc,h} generated by proto_library(). This
# config is propagated up to the source sets that depend on generated proto
# headers and proto libraries. Therefore this should stay as lean and clean as
# possible in terms of -W-no* suppressions. Thankfully the autogenerated .pb.h
# headers violate less warnings than the libprotobuf_* library itself.
# This config is defined here (as opposed to //buildtools/BUILD.gn) so that when
# perfetto_use_system_protobuf=true, the right compiler flags are passed.
config("protobuf_gen_config") {
  visibility = [ "*" ]  # This is injected by standalone/proto_library.gni
  defines = [
    "GOOGLE_PROTOBUF_NO_RTTI",
    "GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
  ]
  cflags = []
  if (is_gcc) {
    cflags += [ "-Wno-deprecated-declarations" ]
  }
  if (is_clang && is_win) {
    cflags += [
      "-Wno-reserved-id-macro",
      "-Wno-language-extension-token",
      "-Wno-sign-conversion",
      "-Wno-suggest-destructor-override",
      "-Wno-undefined-reinterpret-cast",
      "-Wno-inconsistent-missing-destructor-override",
      "-Wno-unused-parameter",
      "-Wno-shadow-field-in-constructor",
      "-Wno-zero-as-null-pointer-constant",

      # Fixed in upstream protobuf v3.22.0
      # d37cbfd4485f("Update inlined_string_field.h"), but we don't have that.
      "-Wno-undef",
    ]
  }
  if (!is_clang && is_win) {
    # MSVC
    cflags += [
      "/wd4838",  # conversion from 'unsigned long' to 'int' requires a
                  # narrowing conversion
    ]
  }

  if (!perfetto_use_system_protobuf) {
    cflags += [
      # Using -isystem instead of include_dirs (-I), so we don't need to
      # suppress warnings coming from libprotobuf headers. Doing so would mask
      # warnings in our own code.
      perfetto_isystem_cflag,
      rebase_path("../buildtools/protobuf/src", root_build_dir),
    ]

    # Protobuf v22+ headers include abseil headers.
    include_dirs = [ "../buildtools/abseil-cpp" ]
  }
}

# The Google C++ Benchmark library.
# Only available in standalone builds.
if (enable_perfetto_benchmarks) {
  group("benchmark") {
    testonly = true
    public_deps = [ "//buildtools:benchmark" ]
  }
}

# Libbacktrace, used for printing stack traces from crash handler, only in
# standalone debug builds.
if (perfetto_build_standalone && (is_linux || is_android)) {
  group("libbacktrace") {
    public_deps = [ "//buildtools:libbacktrace" ]
  }
}

if (enable_perfetto_trace_processor_sqlite) {
  group("sqlite") {
    if (perfetto_root_path == "//") {
      public_deps = [ "//buildtools:sqlite" ]
    } else {
      if (build_with_chromium) {
        public_deps = [ "//third_party/sqlite:sqlite_dev" ]
      } else {
        public_deps = [ "//third_party/sqlite:sqlite" ]
      }
      public_configs = [ ":sqlite_third_party_include_path" ]
    }
  }
}

config("sqlite_third_party_include_path") {
  if (build_with_chromium) {
    include_dirs = [ "//third_party/sqlite/dev" ]
  } else {
    include_dirs = [ "//third_party/sqlite" ]
  }
}

if (enable_perfetto_trace_processor_mac_instruments) {
  group("expat") {
    if (perfetto_root_path == "//") {
      public_deps = [ "//buildtools:expat" ]
    } else {
      public_deps = [ "//third_party/expat:expat" ]
    }
  }
}

if (enable_perfetto_grpc) {
  group("grpc") {
    public_configs = [ "//buildtools:grpc_public_config" ]
    public_deps = [ "//buildtools/grpc:grpc++" ]
  }

  group("cpp_httplib") {
    public_deps = [ "//buildtools:cpp_httplib" ]
  }
}

if (enable_perfetto_trace_processor_linenoise) {
  # Used by the trace_processor_shell for REPL history.
  # Only available in standalone builds.
  group("linenoise") {
    public_deps = [ "//buildtools:linenoise" ]
  }
}  # if (enable_perfetto_trace_processor_linenoise)

# Only used by src/profiling in standalone and android builds.
if (enable_perfetto_heapprofd || enable_perfetto_traced_perf) {
  group("libunwindstack") {
    public_configs = [ "//buildtools:libunwindstack_config" ]
    public_deps = [ "//buildtools:libunwindstack" ]
  }
}

# Used by src/profiling/perf for perf_regs.h.
if (enable_perfetto_traced_perf) {
  group("bionic_kernel_uapi_headers") {
    public_configs = [ "//buildtools:bionic_kernel_uapi_headers" ]
  }
}

config("system_zlib_config") {
  libs = [ "z" ]
}

# Zlib is used both by trace_processor and by perfetto_cmd.
if (enable_perfetto_zlib) {
  group("zlib") {
    if (perfetto_use_system_zlib) {
      public_configs = [ "//gn:system_zlib_config" ]
    } else if (perfetto_root_path == "//") {
      public_configs = [ "//buildtools:zlib_config" ]
      public_deps = [ "//buildtools:zlib" ]
    } else {
      public_configs = [ "//third_party/zlib:zlib_config" ]
      public_deps = [ "//third_party/zlib" ]
    }
  }
}

if (enable_perfetto_llvm_demangle) {
  group("llvm_demangle") {
    public_deps = [ "//buildtools:llvm_demangle" ]
  }
}

# Allows overriding platform-specific functionality used by base at a
# build-system level. This allows e.g. different implementations of base
# functions in Google3.
group("base_platform") {
  public_deps = [ "../src/base:perfetto_base_default_platform" ]
}

# Used by fuzzers.
if (enable_perfetto_fuzzers && use_libfuzzer) {
  group("libfuzzer") {
    assert(perfetto_root_path == "//")
    public_deps = [ "//buildtools:libfuzzer" ]
  }
}

# Python libraries which need to be installed on the system
# or provided (for other build systems).
perfetto_py_library("pandas_py") {
}
perfetto_py_library("tp_vendor_py") {
}
perfetto_py_library("tp_resolvers_py") {
}
perfetto_py_library("protobuf_py") {
}

# Android Java SDK libraries wich need to be provided by other build systems.
perfetto_android_library("android_test_common") {
}

if (enable_perfetto_etm_importer) {
  group("open_csd") {
    visibility = [ "//src/trace_processor/importers/etm" ]
    public_deps = [ "//buildtools:open_csd" ]
  }
}
