# 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("//gn/perfetto_check_build_deps.gni")
import("//gn/standalone/android.gni")
import("//gn/standalone/libc++/libc++.gni")
import("//gn/standalone/sanitizers/sanitizers.gni")
import("//gn/standalone/toolchain/msvc.gni")
import("//gn/standalone/wasm.gni")

# These warnings have been introduced with the newest version of clang (only in
# the hermetic build) and are enabled just with -Werror.
hermetic_clang_suppressions = [ "-Wno-c99-designator" ]

# We deal with three toolchains here:
# 1. Clang, used in most cases.
# 2. GCC, used in some Linux cases.
# 3. MSVC, used in some Windows cases.
# Clang vs gcc is typically not a problem: both support roughly the same
# switches. -Wno-unknown-warning-option fixes the mismatching ones.
# The situation on Windows is a bit trickier: clang-cl.exe really pretends to be
# cl.exe (MSVC), so we should use MSVC-style switches (e.g. /W2). However,
# clang-cl.exe still supports some -Wclang-style-switches for flags that don't
# have a corresponding version in MSVC.
#
# In the rules below, the conditionals should be interpreted as follows:
# is_win -> can be either clang-cl.exe or cl.exe (MSVC). Only MSVC-style
#           switches (the common denominator) should be used.
# is_clang -> could be clang-on-linux, clang-on-mac or clang-cl.exe.

config("extra_warnings") {
  if (is_win) {
    cflags = [
      "/W2",
      "/wd4244",  # conversion from 'float' to 'int', possible loss of data
      "/wd4267",  # conversion from 'size_t' to 'int', possible loss of data
    ]
    if (is_clang) {
      cflags += [
        "-Wno-float-equal",
        "-Wno-unused-macros",
        "-Wno-old-style-cast",
        "-Wno-nonportable-system-include-path",
      ]
    }
  } else {
    # Clang or Gcc. On linux, Android and Mac.
    cflags = [
      "-Wall",
      "-Wextra",
      "-Wpedantic",
    ]

    # Apple Clang tends to lag upstream Clang by several major versions, so add
    # additional warnings on Mac to get warning behavior that more closely
    # mimics the newer Clang used in Perfetto CI on Linux.
    if (is_mac && is_clang) {
      cflags += [
        "-Wextra-semi",
        "-Wsign-conversion",
      ]
    }
  }

  # Disable variadic macro warning as we make extensive use of them in trace
  # processor and client API.
  if (is_clang) {
    # Only enable -Weverything on hermetic clang as system clang might be quite
    # out of date.
    if (is_hermetic_clang && current_toolchain == host_toolchain &&
        !is_fuzzer) {
      # Disable Weverything on fuzzers to avoid breakages when new versions of
      # clang are rolled into OSS-fuzz.
      cflags += [ "-Weverything" ]
    }
    cflags += [
      "-Wno-c++98-compat-pedantic",
      "-Wno-c++98-compat",
      "-Wno-c2y-extensions",
      "-Wno-disabled-macro-expansion",
      "-Wno-documentation-unknown-command",
      "-Wno-gnu-include-next",
      "-Wno-gnu-statement-expression",
      "-Wno-gnu-zero-variadic-macro-arguments",
      "-Wno-padded",
      "-Wno-poison-system-directories",
      "-Wno-pre-c11-compat",
      "-Wno-c2y-extensions",
      "-Wno-reserved-id-macro",
      "-Wno-reserved-identifier",
      "-Wno-shadow-uncaptured-local",
      "-Wno-unknown-sanitizers",
      "-Wno-unknown-warning-option",
      "-Wno-unsafe-buffer-usage",

      # clang's lifetime-safety analysis (enabled by -Weverything) is
      # experimental and fires on hundreds of well-established patterns across
      # the codebase, e.g. getters returning references to members, move
      # constructors moving from by-value parameters or vectors being resized
      # before a pointer into their buffer is taken. It is not enabled by
      # default (not even in -Wall) and is too noisy to adopt. See
      # https://reviews.llvm.org/D111469 for the design of the analysis.
      "-Wno-lifetime-safety-intra-tu-suggestions",
      "-Wno-lifetime-safety-cross-tu-suggestions",
      "-Wno-lifetime-safety-invalidation",
      "-Wno-lifetime-safety-lifetimebound-violation",
      "-Wno-lifetime-safety-intra-tu-misplaced-lifetimebound",
      "-Wno-lifetime-safety-return-stack-addr",
      "-Wno-lifetime-safety-return-stack-addr-moved",
      "-Wno-lifetime-safety-dangling-field-moved",
      "-Wno-lifetime-safety-use-after-scope-moved",
      "-Wno-lifetime-safety-dangling-global",

      # TODO(lalitm): fires on function-local statics (and thread_locals) in
      # inline functions defined in headers, a pattern used in several hot
      # paths (e.g. tp_metatrace's RingBuffer, the C SDK's GlobalState,
      # absl's raw_hash_set). The statics would only be duplicated if the same
      # header were compiled into multiple shared libraries loaded in one
      # process, which no Perfetto build does today; the proper fix would be
      # moving the statics into .cc files (keeping the inline functions), but
      # that has to be balanced against the performance cost of outlining.
      "-Wno-unique-object-duplication",

      # Fires on any function returning a std::string by value with multiple
      # return paths (NRVO cannot apply). At -O3 the compiler usually elides
      # the copy anyway, so this is only a style nit.
      "-Wno-nrvo",

      # TODO(primiano): -Wswitch-default could be useful but will require a mass
      # codebase cleanup.
      "-Wno-switch-default",
    ]

    # This is required for at least upstream and FreeBSD clang 19.
    # See https://github.com/llvm/llvm-project/issues/76375 for more
    # information.
    if (is_freebsd) {
      cflags += [
        "-Wno-c23-extensions",
        "-Wno-c++20-extensions",
      ]
    }

    if (perfetto_thread_safety_annotations) {
      cflags += [
        "-Wthread-safety",
        "-Wno-thread-safety-negative",
      ]
    }
  } else if (is_gcc) {
    # Use return std::move(...) for compatibility with old GCC compilers.
    cflags_cc = [ "-Wno-redundant-move" ]

    # Use after free detection in GCC is still not good enough: it still fails
    # on very obvious false-positives in trace processor.
    cflags_cc += [ "-Wno-use-after-free" ]

    # GCC 7's handling of uninitialized std::optional is flaky at best and
    # causes many false positives.
    # TODO(lalitm): remove this when we upgrade to a GCC version which is good
    # enough to handle this.
    cflags_cc += [ "-Wno-maybe-uninitialized" ]

    # GCC's handling of detecting infinite recursion is flaky at best and
    # causes some false positives.
    # TODO(lalitm): remove this when we upgrade to a GCC version which is good
    # enough to handle this.
    cflags_cc += [ "-Wno-infinite-recursion" ]

    # GCC's handling of detecting non null arguments is flaky at best and
    # causes some false positives.
    # TODO(lalitm): remove this when we upgrade to a GCC version which is good
    # enough to handle this.
    cflags_cc += [ "-Wno-nonnull" ]
  }
}

config("no_exceptions") {
  # Exceptions are disabled by default on Windows (Use /EHsc to enable them).
  if (!is_win) {
    cflags_cc = [ "-fno-exceptions" ]
  }
}

config("rtti_cflags") {
  # Generally, we want -fno-rtti. However, this is compatible with some ubsan
  # configurations (specifically, those that use -fsanitize=vptr). Since we
  # don't know if anyone linking in perfetto will use -fsanitize=vptr, don't
  # pass -fno-rtti when is_ubsan is true.
  if (!is_ubsan) {
    if (is_win) {
      cflags_cc = [ "/GR-" ]
    } else {
      cflags_cc = [ "-fno-rtti" ]
    }
  }
}

# Used in buildtools dependencies for standalone builds.
config("c++17") {
  if (is_win) {
    cflags_cc = [ "/std:c++17" ]
  } else {
    cflags_cc = [ "-std=c++17" ]
  }
}

# Used in buildtools dependencies for standalone builds.
config("c++20") {
  visibility = [ "//buildtools:libc++config" ]
  if (is_win) {
    cflags_cc = [ "/std:c++20" ]
  } else {
    cflags_cc = [ "-std=c++20" ]
  }
}

config("visibility_hidden") {
  if (!is_win) {
    cflags = [ "-fvisibility=hidden" ]
  }
}

config("win32_lean_and_mean") {
  if (is_win) {
    defines = [ "WIN32_LEAN_AND_MEAN" ]
  }
}

config("default") {
  asmflags = []
  cflags = []
  cflags_c = []
  cflags_cc = []
  defines = []
  include_dirs = []
  ldflags = []
  libs = []

  if ((is_android || is_linux || is_qnx) && !is_wasm) {
    ldflags += [
      "-Wl,--build-id",
      "-Wl,-z,max-page-size=16384",
    ]
  }

  if (is_clang || !is_win) {  # Clang or GCC, but not MSVC.
    cflags += [
      "-fstrict-aliasing",
      "-Wformat",
    ]
  }

  if (is_clang && is_win) {
    # clang-cl from version 16 does not like out-of-line definition of static
    # constexpr, even in C++14 mode. Disable the deprecated warnings to work
    # around the problem.
    cflags += [ "-Wno-deprecated" ]
  }

  if (!is_clang && is_win) {
    # MSVC
    cflags += [ "/permissive-" ]
  }

  if (is_win) {
    cflags += [
      "/bigobj",  # Some of our files are bigger than the regular limits.
      "/Gy",  # Enable function-level linking.
      "/FS",  # Preserve previous PDB behavior.
      "/utf-8",  # Assume UTF-8 by default to avoid code page dependencies.
      "/Zc:__cplusplus",  # Allow use of __cplusplus macro.
    ]
    defines += [
      "_CRT_NONSTDC_NO_WARNINGS",
      "_CRT_SECURE_NO_DEPRECATE",
      "_CRT_SECURE_NO_WARNINGS",  # Disables warnings on some POSIX-compat API.
      "_SCL_SECURE_NO_DEPRECATE",
      "NOMINMAX",
    ]
    if (!use_custom_libcxx) {
      defines += [ "_HAS_EXCEPTIONS=0" ]  # Disables exceptions in MSVC STL.
    }
  } else if (!is_wasm) {  # !is_win
    cflags += [
      "-g",
      "-fPIC",
      "-fstack-protector-strong",
    ]
  }

  # Treat warnings as errors, but give up on fuzzer and non-hermetic builds.
  if (!is_fuzzer && is_hermetic_clang) {
    if (is_win) {
      cflags += [ "/WX" ]
    } else {
      cflags += [ "-Werror" ]
    }
  }

  if (is_clang) {
    cflags += [ "-fcolor-diagnostics" ]
    if (!is_win) {
      cflags += [ "-fdiagnostics-show-template-tree" ]
    }
  }

  if (is_hermetic_clang && is_linux && !is_wasm) {
    cflags += hermetic_clang_suppressions
  } else {
    not_needed([ "hermetic_clang_suppressions" ])
  }

  if (non_hermetic_clang_stdlib != "") {
    if (is_clang && !is_hermetic_clang && !is_wasm) {
      cflags_cc += [ "-stdlib=" + non_hermetic_clang_stdlib ]
      ldflags += [ "-stdlib=" + non_hermetic_clang_stdlib ]
    }
  }

  if (is_lto) {
    cflags += [ "-flto=full" ]
    ldflags += [ "-flto=full" ]
  }

  if (is_win) {
    # We support only x86/x64 builds on Windows.
    assert(current_cpu == "x64" || current_cpu == "x86")
  } else if (current_cpu == "arm") {
    cflags += [
      "-march=armv7-a",
      "-mfpu=neon",
      "-mthumb",
    ]
  } else if (current_cpu == "riscv64") {
    if (!is_clang) {
      # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104338
      libs += [ "atomic" ]
    }
  } else if (current_cpu == "x86") {
    asmflags += [ "-m32" ]
    cflags += [
      "-m32",
      "-msse2",
      "-mfpmath=sse",
    ]
    ldflags += [ "-m32" ]
  } else if (current_cpu == "arm64") {
    cflags += [ "-fno-omit-frame-pointer" ]
  } else if (current_cpu == "x64") {
    cflags += [ "-fno-omit-frame-pointer" ]  # For perf profiling.
  }

  if (is_wasm) {
    # -msimd128 enables WASM SIMD128 and is safe for all WASM targets. -mavx2
    # lets x86 AVX2 intrinsics compile, emulated on top of SIMD128.
    cflags += [
      "-msimd128",
      "-mavx2",
    ]
  }

  if (is_wasm_memory64) {
    cflags += [
      "-s",
      "MEMORY64=1",
    ]
  }

  if (is_linux) {
    # Enable LFS (large file support) for stat() and other syscalls.
    cflags += [
      "-D_FILE_OFFSET_BITS=64",
      "-D_LARGEFILE_SOURCE",
      "-D_LARGEFILE64_SOURCE",
    ]
    libs += [
      "pthread",
      "rt",
    ]
  }

  if (is_freebsd) {
    libs += [
      "pthread",
      "rt",
    ]
  }

  if (is_qnx) {
    cflags += [
      "-D_QNX_SOURCE",
      "-D_FILE_OFFSET_BITS=64",
      "-D_LARGEFILE_SOURCE",
      "-D_LARGEFILE64_SOURCE",
    ]
    libs += [ "socket" ]
  }

  if (is_win && !is_clang) {
    # When using MSVC we need to manually pass the include dirs. clang-cl.exe
    # doesn't need them because it's smart enough to figure out the right path
    # by querying the registry on its own.
    include_dirs = win_msvc_inc_dirs  # Defined in msvc.gni.
  }

  if (is_win) {
    cflags += [ "/Zi" ]
  }
  if (is_debug) {
    if (is_win) {
      if (is_clang) {
        # Required to see symbols in windbg when building with clang-cl.exe.
        cflags += [ "-gcodeview-ghash" ]
        ldflags = [ "/DEBUG:GHASH" ]
      }
    } else if (!is_qnx) {
      libs += [ "dl" ]
    }
  }

  if (is_android) {
    asmflags += [ "--target=$android_abi_target" ]
    cflags += [
      "--sysroot=$android_compile_sysroot",
      "-DANDROID",
      "-D__ANDROID_API__=${android_api_level}",
      "--target=$android_abi_target",
    ]
    cflags_cc += [ "-isystem$android_compile_sysroot/c++/v1" ]

    android_lib_dir = "$android_compile_sysroot/usr/lib/$android_abi_target/$android_api_level"
    ldflags += [
      "-Wl,-z,nocopyreloc",
      "--sysroot=$android_compile_sysroot",
      "-B${android_lib_dir}",
      "--target=$android_abi_target",
      "-Wl,--no-undefined",
      "-Wl,-z,noexecstack",
      "-Wl,-z,relro",
      "-Wl,-z,now",
      "-Wl,--warn-shared-textrel",
      "-Wl,--fatal-warnings",

      # From NDK docs: "although the option uses the name "libstdc++" for
      # historical reasons, this is correct for libc++ as well.
      "-static-libstdc++",
    ]
    lib_dirs = [ android_lib_dir ]
  }
}

# x64 CPU optimization flags (SSE4.2, AVX2, BMI2, LZCNT). Separate config so the
# startup CPU check can opt out of it. Keep in sync with CheckCpuOptimizations().
config("x64_cpu_opt") {
  if (current_cpu == "x64" && enable_perfetto_x64_cpu_opt) {
    cflags = [
      "-mbmi",
      "-mbmi2",
      "-mavx2",
      "-mpopcnt",
      "-mlzcnt",
      "-msse4.2",
    ]
  }
}

config("debug_noopt") {
  cflags = []
  if (is_win) {
    cflags = [ "/Od" ]
  } else {
    cflags = [ "-O0" ]
  }
  if (is_android || is_linux) {
    cflags += [ "-funwind-tables" ]
  }
}

config("release") {
  # Compiler flags for release builds.
  if (is_win) {
    cflags = [
      "/O2",
      "/Zc:inline",
    ]
  } else if (is_android) {
    cflags = [ "-O2" ]
  } else if (is_fuzzer) {
    cflags = [ "-O1" ]
  } else if (is_qnx && current_cpu == "x64") {
    # -02 on x64 causes on QNX8.0 x86_64 -Wno-error=alloc-size-larger-than
    cflags = [ "-O1" ]
  } else {
    cflags = [ "-O3" ]
  }
  if (!is_win) {
    cflags += [
      "-fdata-sections",
      "-ffunction-sections",
    ]
  }

  # Linker flags for release builds.
  if (is_win) {
    ldflags = [
      "/OPT:REF",
      "/OPT:ICF",
      "/INCREMENTAL:NO",
      "/FIXED:NO",
    ]
  } else if (is_mac && !is_wasm) {
    ldflags = [ "-dead_strip" ]
  } else if (is_qnx) {
    # QNX doesn't support the --icf linker option
    ldflags = [
      "-Wl,--gc-sections",
      "-Wl,-O1",
    ]
  } else if (!is_wasm && (is_android || (is_linux && is_hermetic_clang))) {
    ldflags = [
      "-Wl,--gc-sections",
      "-Wl,--icf=all",
      "-Wl,-O1",
    ]
  }
  defines = [ "NDEBUG" ]
}

config("shared_library") {
  if (is_android || is_linux) {
    ldflags = [ "-fPIC" ]
  }
}

config("executable") {
  ldflags = []

  # Android will refuse to run executables if they aren't position independent.
  # Instead on Linux there isn't any need and they break ASan (goo.gl/paFR6K).
  # The OSS-Fuzz provided AFL library is not PIC, so we we cannot use -fPIE
  # for the fuzzer executables.
  if ((is_android || is_linux) && !is_wasm && !is_fuzzer) {
    asmflags = [ "-fPIE" ]
    cflags = [ "-fPIE" ]
    ldflags += [ "-pie" ]
  }

  # -rpath stores the path to the linked shared libraries into the binary, so
  # that they can be launched without passing any LD_LIBRARY_PATH. It's
  # supported only by Linux, not Android. But concretely we need this only when
  # use_custom_libcxx=true && custom_libcxx_is_static=false, which happens only
  # on Linux right now.
  if (is_linux && !is_wasm) {
    ldflags += [
      "-Wl,-rpath=\$ORIGIN/.",
      "-Wl,-rpath-link=.",
    ]
  }
}

# This config is only added to certain leaf target types (see BUILDCONFIG.gn).
# This allows us to remove the config (and thus the dependency) on a per-target
# basis. If the config was applied to source_sets, then they would unavoidably
# carry the dependency onto liblog to the final target.
config("android_liblog") {
  if (is_android) {
    libs = [ "log" ]
  }
}

# Checks that tools/install-build-deps has been run since it last changed.
perfetto_check_build_deps("check_build_deps") {
  args = [ "--no-dev-tools" ]
}

perfetto_check_build_deps("check_build_deps_android") {
  args = [
    "--no-dev-tools",
    "--android",
  ]
}
