# Copyright 2013 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/config/pch.gni")
import("//build/config/rust.gni")
import("clang.gni")

if (is_ios) {
  # For `target_environment` and `target_platform`.
  import("//build/config/apple/mobile_config.gni")
}

# Helper function for adding cflags to use a clang plugin.
# - `plugin` is the name of the plugin.
# - `plugin_arguments` is a list of arguments to pass to the plugin.
template("clang_plugin") {
  config(target_name) {
    forward_variables_from(invoker,
                           [
                             "cflags",
                             "configs",
                           ])
    if (!defined(cflags)) {
      cflags = []
    }

    if (defined(invoker.plugin)) {
      cflags += [
        "-Xclang",
        "-add-plugin",
        "-Xclang",
        invoker.plugin,
      ]
    }

    if (defined(invoker.plugin_arguments)) {
      foreach(flag, invoker.plugin_arguments) {
        cflags += [
          "-Xclang",
          "-plugin-arg-${invoker.plugin}",
          "-Xclang",
          flag,
        ]
      }
    }
  }
}

clang_plugin("raw_ptr_check") {
  if (clang_use_chrome_plugins || clang_use_raw_ptr_plugin) {
    # The plugin is built directly into clang, so there's no need to load it
    # dynamically.
    plugin = "raw-ptr-plugin"
    plugin_arguments = [
      "check-raw-ptr-to-stack-allocated",
      "disable-check-raw-ptr-to-stack-allocated-error",

      # TODO(crbug.com/40944547): Remove when raw_ptr check has been enabled
      # for the dawn repo.
      "raw-ptr-exclude-path=" +
          rebase_path("//third_party/dawn/", root_build_dir),

      # TODO(crbug.com/417356903): RawRefFieldMatcher causes OOM while
      # building webidl_in_process_fuzzer.
      "raw-ptr-exclude-path=" +
          rebase_path("chrome/test/fuzzing/webidl_fuzzing/", root_build_dir),
    ]

    if (enable_check_raw_ptr_fields) {
      plugin_arguments += [
        "check-raw-ptr-fields",
        "check-span-fields",
      ]
    }

    if (enable_check_raw_ref_fields) {
      plugin_arguments += [ "check-raw-ref-fields" ]
    }
  }
}

clang_plugin("find_bad_constructs") {
  if (clang_use_chrome_plugins) {
    # The plugin is built directly into clang, so there's no need to load it
    # dynamically.
    plugin = "find-bad-constructs"
    plugin_arguments = [ "check-stack-allocated" ]

    if (is_linux || is_chromeos || is_android || is_fuchsia) {
      plugin_arguments += [ "check-ipc" ]
    }

    configs = [ ":raw_ptr_check" ]
  }
}

# A plugin for incrementally applying the -Wunsafe-buffer-usage warning.
#
# To use the plugin, the project must specify a path as
# `clang_unsafe_buffers_paths` in the `//.gn` file. This path points to a text
# file that controls where the warning is checked.
#
# See //build/config/unsafe_buffers_paths.txt for an example file, this it the
# file used by Chromium.
#
# This build configuration is not supported when `enable_precompiled_headers`
# is on because the pragmas that enable and disable unsafe-buffers warnings are
# not serialized to precompiled header files, and thus we get warnings that we
# should not.
clang_plugin("unsafe_buffers") {
  if (clang_use_chrome_plugins || clang_use_unsafe_buffers_plugin) {
    if (clang_unsafe_buffers_paths != "" && !enable_precompiled_headers) {
      cflags = [ "-DUNSAFE_BUFFERS_BUILD" ]
      plugin = "unsafe-buffers"
      plugin_arguments =
          [ rebase_path(clang_unsafe_buffers_paths, root_build_dir) ]
    }
  }
}

# Enables some extra Clang-specific warnings. Some third-party code won't
# compile with these so may want to remove this config.
config("extra_warnings") {
  cflags = [
    "-Wheader-hygiene",

    # Warns when a const char[] is converted to bool.
    "-Wstring-conversion",

    "-Wtautological-overlap-compare",
  ]
}

group("llvm-symbolizer_data") {
  if (is_win) {
    data = [ "$clang_base_path/bin/llvm-symbolizer.exe" ]
  } else {
    data = [ "$clang_base_path/bin/llvm-symbolizer" ]
  }
}

template("clang_lib") {
  if (!defined(invoker.libname) || is_wasm) {
    not_needed(invoker, "*")
    config(target_name) {
    }
  } else {
    config(target_name) {
      _dir = ""
      _libname = invoker.libname
      _prefix = "lib"
      _suffix = ""
      _ext = "a"

      if (is_win) {
        _dir = "windows"
        _prefix = ""
        _ext = "lib"
        if (current_cpu == "x64") {
          _suffix = "-x86_64"
        } else if (current_cpu == "x86") {
          _suffix = "-i386"
        } else if (current_cpu == "arm64") {
          _suffix = "-aarch64"
        } else {
          assert(false)  # Unhandled cpu type
        }
      } else if (is_apple) {
        _dir = "darwin"
      } else if (is_linux || is_chromeos) {
        if (current_cpu == "x64") {
          _dir = "x86_64-unknown-linux-gnu"
        } else if (current_cpu == "x86") {
          _dir = "i386-unknown-linux-gnu"
        } else if (current_cpu == "arm") {
          _dir = "armv7-unknown-linux-gnueabihf"
        } else if (current_cpu == "arm64") {
          _dir = "aarch64-unknown-linux-gnu"
        } else if (current_cpu == "loong64") {
          _dir = "loongarch64-unknown-linux-gnu"
        } else if (current_cpu == "riscv64") {
          _dir = "riscv64-unknown-linux-gnu"
        } else if (current_cpu == "ppc64") {
          _dir = "ppc64le-unknown-linux-gnu"
        } else if (current_cpu == "s390x") {
          _dir = "s390x-unknown-linux-gnu"
        } else {
          assert(false)  # Unhandled cpu type
        }
      } else if (is_fuchsia) {
        if (current_cpu == "x64") {
          _dir = "x86_64-unknown-fuchsia"
        } else if (current_cpu == "arm64") {
          _dir = "aarch64-unknown-fuchsia"
        } else {
          assert(false)  # Unhandled cpu type
        }
      } else if (is_android) {
        _dir = "linux"
        if (current_cpu == "x64") {
          _suffix = "-x86_64-android"
        } else if (current_cpu == "x86") {
          _suffix = "-i686-android"
        } else if (current_cpu == "arm") {
          _suffix = "-arm-android"
        } else if (current_cpu == "arm64") {
          _suffix = "-aarch64-android"
        } else if (current_cpu == "riscv64") {
          _suffix = "-riscv64-android"
        } else {
          assert(false)  # Unhandled cpu type
        }
      } else {
        assert(false)  # Unhandled target platform
      }

      _clang_lib_dir = "$clang_base_path/lib/clang/$clang_version/lib"
      _lib_file = "${_prefix}clang_rt.${_libname}${_suffix}.${_ext}"
      libs = [ "$_clang_lib_dir/$_dir/$_lib_file" ]
    }
  }
}

# Adds a dependency on the Clang runtime library clang_rt.builtins.
clang_lib("compiler_builtins") {
  if (is_mac) {
    libname = "osx"
  } else if (is_ios) {
    if (target_platform == "iphoneos") {
      if (target_environment == "simulator") {
        libname = "iossim"
      } else if (target_environment == "device") {
        libname = "ios"
      } else if (target_environment == "catalyst") {
        libname = "osx"
      } else {
        assert(false, "unsupported target_environment=$target_environment")
      }
    } else if (target_platform == "tvos") {
      if (target_environment == "simulator") {
        libname = "tvossim"
      } else if (target_environment == "device") {
        libname = "tvos"
      } else {
        assert(false, "unsupported target_environment=$target_environment")
      }
    } else {
      assert(false, "unsupported target_platform=$target_platform")
    }
  } else if (current_cpu != "ppc64" && current_cpu != "s390x") {
    libname = "builtins"
  }
}

# Adds a dependency on the Clang runtime library clang_rt.profile.
clang_lib("compiler_profile") {
  if (!toolchain_has_rust) {
    # This is only used when `toolchain_has_rust` to support Rust linking.
    #
    # Don't define libname which makes this target do nothing.
  } else if (is_mac) {
    libname = "profile_osx"
  } else if (is_ios) {
    if (target_environment == "simulator") {
      libname = "profile_iossim"
    } else if (target_environment == "catalyst") {
      # We don't enable clang coverage on iOS device builds, and the library is
      # not part of the Clang package tarball as a result.
      #
      # Don't define libname which makes this target do nothing.
    } else {
      # We don't enable clang coverage on iOS device builds, and the library is
      # not part of the Clang package tarball as a result.
      #
      # Don't define libname which makes this target do nothing.
    }
  } else {
    libname = "profile"
  }
}
