# Copyright (c) 2021 Huawei Device Co., Ltd.
# 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("//build/config/ohos/copy_ex.gni")
import("//build/config/python.gni")
import("//build/ohos/build_var.gni")
import("//build/ohos/notice/notice.gni")
import("//build/ohos_var.gni")

declare_args() {
  sdk_native = "sdk-native"
  version_script_suffix = ".map.txt"
  ndk_signature_save_dir = "//interface/sdk-native"
  ndk_zip_prefix = "native"
  enable_ndk_doxygen = true
}

if (use_current_sdk) {
  # native_dir is native current-sdk release dir, all things redefine base the value.
  native_dir = "base-current-sdk/linux/${api_version}/native"
  if (point_split) {
    native_dir = "base-current-sdk/linux/${api_full_version}/native"
  }

  ndk_os_irrelevant_out_dir = "$root_out_dir/${native_dir}"
  ndk_os_specific_out_dir = "$root_out_dir/${native_dir}"
  ndk_signature_out_dir = "$root_out_dir/${native_dir}/.others/signature"
} else {
  ndk_os_irrelevant_out_dir = "$root_out_dir/${sdk_native}/os-irrelevant"
  ndk_os_specific_out_dir = "$root_out_dir/${sdk_native}/os-specific"
  ndk_signature_out_dir = "$root_out_dir/${sdk_native}/signature"
}

ndk_headers_out_dir = "$ndk_os_irrelevant_out_dir/sysroot/usr/include"
ndk_libraries_out_dir = "$ndk_os_irrelevant_out_dir/sysroot/usr/lib"
ndk_docs_out_dir = "$ndk_os_irrelevant_out_dir/docs"

windows_system = "windows"
linux_system = "linux"
darwin_system = "darwin"
ohos_system = "ohos"
linux_arm64_system = "linux-arm64"

if (use_current_sdk) {
  ndk_windows_specific_out_dir =
      "${ndk_os_specific_out_dir}/.others/${windows_system}"
  ndk_darwin_specific_out_dir =
      "${ndk_os_specific_out_dir}/.others/${darwin_system}"
  ndk_linux_specific_out_dir = "${ndk_os_specific_out_dir}"
  ndk_ohos_specific_out_dir =
      "${ndk_os_specific_out_dir}/.others/${ohos_system}"
  ndk_linux_arm64_specific_out_dir =
      "${ndk_os_specific_out_dir}/.others/${linux_arm64_system}"
} else {
  ndk_windows_specific_out_dir = "${ndk_os_specific_out_dir}/${windows_system}"
  ndk_darwin_specific_out_dir = "${ndk_os_specific_out_dir}/${darwin_system}"
  ndk_linux_specific_out_dir = "${ndk_os_specific_out_dir}/${linux_system}"
  ndk_ohos_specific_out_dir = "${ndk_os_specific_out_dir}/${ohos_system}"
  ndk_linux_arm64_specific_out_dir =
      "${ndk_os_specific_out_dir}/${linux_arm64_system}"
}

ndk_windows_toolchains_out_dir = "${ndk_windows_specific_out_dir}/llvm"
ndk_windows_tools_out_dir = "${ndk_windows_specific_out_dir}/build-tools"

ndk_darwin_toolchains_out_dir = "${ndk_darwin_specific_out_dir}/llvm"
ndk_darwin_tools_out_dir = "${ndk_darwin_specific_out_dir}/build-tools"

ndk_linux_toolchains_out_dir = "${ndk_linux_specific_out_dir}/llvm"
ndk_linux_tools_out_dir = "${ndk_linux_specific_out_dir}/build-tools"

ndk_ohos_toolchains_out_dir = "${ndk_ohos_specific_out_dir}/llvm"
ndk_ohos_tools_out_dir = "${ndk_ohos_specific_out_dir}/build-tools"

ndk_linux_arm64_toolchains_out_dir = "${ndk_linux_arm64_specific_out_dir}/llvm"
ndk_linux_arm64_tools_out_dir =
    "${ndk_linux_arm64_specific_out_dir}/build-tools"

# Generate NDK library from NDK description file.
#
# Variables:
#  ndk_description_file:
#  min_compact_version: string specifies the minimal compactible version of NDK.
#    set to major_version in default.
#
template("ohos_ndk_library") {
  forward_variables_from(invoker, [ "testonly" ])
  assert(defined(invoker.ndk_description_file),
         "ndk description file is necessary ")

  _ndk_description_file = invoker.ndk_description_file

  _system_capability = ""
  if (defined(invoker.system_capability)) {
    _system_capability = invoker.system_capability
  }

  _system_capability_headers = ""
  if (defined(invoker.system_capability_headers)) {
    _system_capability_headers = invoker.system_capability_headers
  }

  _deps = []
  if (defined(invoker.deps)) {
    _deps += invoker.deps
  }

  _ndk_config_output = "$target_gen_dir/$target_name.build_config"
  _sdk_version = exec_script("//build/ohos/version.py",
                             [
                               "--version",
                               sdk_version,
                             ],
                             "list lines")
  _min_compact_version = _sdk_version[0]
  if (defined(invoker.min_compact_version)) {
    _min_compact_version = invoker.min_compact_version
  }
  assert(_min_compact_version != "0")  # mark as used

  _output_name = target_name
  if (defined(invoker.output_name)) {
    _output_name = invoker.output_name
  }

  _output_extension = "z.so"
  if (defined(invoker.output_extension)) {
    _output_extension = invoker.output_extension
  }

  _ndk_stub_target = "${target_name}__ndk_stub"
  _generated_ndk_stub_file = target_gen_dir + "/${target_name}.ndk/" +
                             get_path_info(_ndk_description_file, "name") + ".c"
  _current_label = get_label_info(":${target_name}", "label_with_toolchain")
  action_with_pydeps(_ndk_stub_target) {
    deps = _deps
    script = "//build/ohos/ndk/generate_ndk_stub_file.py"
    depfile = "${target_gen_dir}/${target_name}.d"
    args = [
      "--output",
      rebase_path(_generated_ndk_stub_file, root_build_dir),
      "--ndk-description-file",
      rebase_path(_ndk_description_file, root_build_dir),
      "--depfile",
      rebase_path(depfile, root_build_dir),
    ]
    inputs = [ _ndk_description_file ]
    outputs = [ _generated_ndk_stub_file ]

    _ndk_config_info = {
      label = _current_label
      lib_name = _output_name
      system_capability = _system_capability
      system_capability_headers = _system_capability_headers
    }
    metadata = {
      ndk_config = [ _ndk_config_info ]
    }
  }

  _ndk_config_target = "${target_name}__ndk_config"
  generated_file(_ndk_config_target) {
    deps = [ ":$_ndk_stub_target" ]
    output_conversion = "json"
    outputs = [ _ndk_config_output ]

    data_keys = [ "ndk_config" ]
  }

  ndk_toolchains = []
  if (build_ohos_ndk || use_current_sdk) {
    if (host_os == "linux" && host_cpu == "arm64") {
      ndk_toolchains = [ "//build/toolchain/ohos:ohos_clang_arm64" ]
    } else {
      ndk_toolchains = [
        "//build/toolchain/ohos:ohos_clang_arm",
        "//build/toolchain/ohos:ohos_clang_arm64",
        "//build/toolchain/ohos:ohos_clang_x86_64",
      ]
    }
  } else if (!is_arkui_x) {
    # Don't enable cross compile if build_ohos_ndk is false.
    # Cross compiling in this case may cause build failure in some scenario,
    # such as build for ASAN.
    ndk_toolchains = [ "//build/toolchain/ohos:ohos_clang_${target_cpu}" ]
  }

  if (ndk_toolchains == []) {
    not_needed([ "_output_extension" ])
  }

  _accumulated_deps = []

  foreach(_toolchain, ndk_toolchains) {
    if (_toolchain == "//build/toolchain/ohos:ohos_clang_arm") {
      _ndk_shlib_directory = "arm-linux-ohos"
    } else if (_toolchain == "//build/toolchain/ohos:ohos_clang_arm64") {
      _ndk_shlib_directory = "aarch64-linux-ohos"
    } else if (_toolchain == "//build/toolchain/ohos:ohos_clang_x86_64") {
      _ndk_shlib_directory = "x86_64-linux-ohos"
    }

    assert(defined(_ndk_shlib_directory))
    _output_dir = "$ndk_libraries_out_dir/$_ndk_shlib_directory"
    if (defined(invoker.current_ndk_outpath) &&
        invoker.current_ndk_outpath != "" && use_current_sdk) {
      _output_dir = "${invoker.current_ndk_outpath}/${_ndk_shlib_directory}"
    }
    _output_ndk_shlib = "${_output_dir}/lib${_output_name}.${_output_extension}"

    _toolchain_name = get_label_info(_toolchain, "name")

    _ndk_shlib_target = "${target_name}_${_toolchain_name}__ndk_shlib"

    shared_library(_ndk_shlib_target) {
      forward_variables_from(invoker,
                             [
                               "cflags",
                               "ldflags",
                               "configs",
                               "libs",
                               "include_dirs",
                             ])
      deps = [ ":$_ndk_stub_target" ]
      sources = [ _generated_ndk_stub_file ]
      output_dir = target_out_dir + "/$_toolchain_name"
      output_name = _output_name
      output_extension = _output_extension
    }

    _ndk_shlib_copy_target = "${target_name}_${_toolchain_name}__copy"
    copy(_ndk_shlib_copy_target) {
      deps = [ ":$_ndk_shlib_target($_toolchain)" ]
      sources = [ get_label_info(":$_ndk_shlib_target($_toolchain)",
                                 "target_out_dir") +
                  "/$_toolchain_name/lib$_output_name.$_output_extension" ]
      outputs = [ _output_ndk_shlib ]
    }
    _accumulated_deps += [ ":$_ndk_shlib_copy_target" ]
    _accumulated_deps += [ ":$_ndk_shlib_target" ]
  }

  _ndk_version_script_target = target_name
  if (current_toolchain == default_toolchain) {
    # Notice file for different toolchains are the same, it's enough to
    # collect notice file for default toolchain.
    _notice_target = "${target_name}__ndk_libraries_notice"
    collect_notice(_notice_target) {
      forward_variables_from(invoker,
                             [
                               "testonly",
                               "license_as_sources",
                               "license_file",
                             ])
      module_source_dir =
          get_label_info(":${_ndk_version_script_target}", "dir")
      outputs = [ "$ndk_notice_dir/sysroot/usr/lib/lib$_output_name.$_output_extension.txt" ]
    }
    _accumulated_deps += [ ":$_notice_target" ]
  }
  if (defined(invoker.license_file)) {
    not_needed(invoker, [ "license_file" ])
  }
  if (defined(invoker.license_as_sources)) {
    not_needed(invoker, [ "license_as_sources" ])
  }

  _generated_version_script =
      target_gen_dir + "/$target_name" + version_script_suffix
  action_with_pydeps(_ndk_version_script_target) {
    deps = _accumulated_deps
    script = "//build/ohos/ndk/generate_version_script.py"
    depfile = "${target_gen_dir}/${target_name}.d"
    args = [
      "--output",
      rebase_path(_generated_version_script, root_build_dir),
      "--ndk-description-file",
      rebase_path(_ndk_description_file, root_build_dir),
      "--shlib-name",
      _output_name,
      "--depfile",
      rebase_path(depfile, root_build_dir),
    ]
    outputs = [ _generated_version_script ]
  }
}

# Specify an ndk copy target
# NOTE: It's an internal template, not designed for everyone use.
#
# Input variables:
#   dest_dir: Root directory where sources are copied to.
#   sources: List of files and directories to copy to ${dest_dir}.
#
template("ohos_ndk_copy") {
  assert(defined(invoker.sources) && defined(invoker.dest_dir),
         "sources and dest_dir are necessary ")

  _deps = []
  if (defined(invoker.deps)) {
    _deps += invoker.deps
  }
  _dest = invoker.dest_dir

  if (defined(invoker.current_ndk_outpath) &&
      invoker.current_ndk_outpath != "" && use_current_sdk) {
    _dest = invoker.current_ndk_outpath
  }

  sources = filter_exclude([ _dest ], [ "*os-irrelevant*" ])
  if (sources == []) {
    _notice_rel_dir = ndk_os_irrelevant_out_dir
  } else {
    _notice_rel_dir = ndk_os_specific_out_dir
  }
  sources = []

  _main_target_name = target_name
  _notice_target = "${target_name}__notice"
  collect_notice(_notice_target) {
    forward_variables_from(invoker,
                           [
                             "testonly",
                             "license_as_sources",
                             "license_file",
                           ])
    module_source_dir = get_label_info(":${_main_target_name}", "dir")
    outputs = []

    foreach(s, invoker.sources) {
      outputs += [ ndk_notice_dir + "/" + rebase_path(_dest, _notice_rel_dir) +
                   "/" + get_path_info(s, "file") + ".txt" ]
    }
  }
  _deps += [ ":$_notice_target" ]
  _clear_dest = true
  if (defined(invoker.clear_dest) && !invoker.clear_dest) {
    _clear_dest = false
  }

  copy_ex(target_name) {
    forward_variables_from(invoker,
                           [
                             "testonly",
                             "visibility",
                           ])
    forward_variables_from(invoker, [ "outputs" ])
    deps = _deps
    sources = invoker.sources

    if (!defined(outputs)) {
      outputs = []
      foreach(src, invoker.sources) {
        _all_files = []
        _all_files =
            exec_script("//build/scripts/find.py",
                        [
                          rebase_path(src, root_build_dir),
                          "--base-dir=" + rebase_path(src, root_build_dir),
                          "--return-relpath",
                          "--follow-symlinks",
                        ],
                        "list lines")

        if (_all_files == [ "." ]) {
          outputs += [ _dest + "/" + get_path_info(src, "file") ]
        } else {
          foreach(f, _all_files) {
            outputs += [ _dest + "/" + get_path_info(src, "name") + "/$f" ]
          }
        }
      }
    }

    dest = _dest
    depfile = "$target_gen_dir/$target_name.d"
    args = [
      "--clear",
      "--follow-outside-symlinks",
      "--depfile",
      rebase_path(depfile, root_build_dir),
      "--stamp",
      rebase_path("$target_gen_dir/$target_name.stamp", root_build_dir),
    ]
    if (!_clear_dest) {
      args -= [ "--clear" ]
    }
    if (defined(invoker.args)) {
      args += invoker.args
    }
  }
}

# Specify ndk header files
#
# Input variables:
#   dest_dir: Root directory where sources are copied to.
#   sources: List of files and directories to copy to ${dest_dir}.
#
template("ohos_ndk_headers") {
  assert(defined(invoker.sources), "sources are necessary ")

  if (defined(invoker.dest_dir)) {
    _dest_dir = invoker.dest_dir
  } else {
    _dest_dir = "$ndk_headers_out_dir"
  }

  if (defined(invoker.current_ndk_outpath) &&
      invoker.current_ndk_outpath != "" && use_current_sdk) {
    _dest_dir = invoker.current_ndk_outpath
  }

  _ndk_header_signature_target = "${target_name}__ndk_signature_check"
  _target_name = target_name
  action_with_pydeps(_ndk_header_signature_target) {
    if (defined(invoker.deps)) {
      deps = invoker.deps
    }

    script = "//build/ohos/ndk/check_ndk_header_signature.py"
    depfile = "${target_gen_dir}/${target_name}.d"

    inputs = []
    foreach(src, invoker.sources) {
      _all_files = []
      _all_files = exec_script("//build/scripts/find.py",
                               [ rebase_path(src) ],
                               "list lines")

      inputs += _all_files
    }

    _output = "$target_gen_dir/$target_name.stamp"

    args = [
      "--depfile",
      rebase_path(depfile, root_build_dir),
      "--generated-signature",
      rebase_path("$ndk_signature_out_dir/$_target_name/signature.txt",
                  root_build_dir),
      "--saved-signature",
      rebase_path("$ndk_signature_save_dir/$_target_name/signature.txt",
                  root_build_dir),
      "--output",
      rebase_path(_output, root_build_dir),
    ]
    foreach(f, inputs) {
      args += [
        "--headers",
        rebase_path(f, root_build_dir),
        "--root-build-dir",
        rebase_path("//", root_build_dir),
      ]
    }

    if (check_ndk_signature) {
      args += [ "--check-signature" ]
    }

    outputs = [ _output ]
  }

  _ndk_header_check_target = "${target_name}__ndk_header_check"
  action_with_pydeps(_ndk_header_check_target) {
    script = "//build/ohos/ndk/check_ndk_header.py"
    inputs = []
    foreach(src, invoker.sources) {
      _all_files = []
      _all_files = exec_script("//build/scripts/find.py",
                               [ rebase_path(src) ],
                               "list lines")

      inputs += _all_files
    }

    _output = "$target_gen_dir/$target_name.gch"
    args = [
      "--output",
      rebase_path(_output, root_build_dir),
    ]
    foreach(f, inputs) {
      args += [
        "--headers",
        rebase_path(f, root_build_dir),
      ]
    }

    outputs = [ _output ]
  }
  ohos_ndk_copy(target_name) {
    forward_variables_from(invoker,
                           "*",
                           [
                             "deps",
                             "args",
                             "dest_dir",
                           ])
    deps = [ ":$_ndk_header_signature_target" ]
    if (defined(ndk_check_header) && target_name != "libuv_uv_header") {
      deps += [ ":$_ndk_header_check_target" ]
    }

    if (defined(invoker.deps)) {
      deps += invoker.deps
    }
    dest_dir = _dest_dir

    args = [ "--ignore-stale" ]
    if (defined(invoker.args)) {
      args += invoker.args
    }
  }
}

# Specify ndk toolchains
#
# Input variables:
#   dest_dir: Root directory where sources are copied to.
#   sources: List of files and directories to copy to ${dest_dir}.
#
template("ohos_ndk_toolchains") {
  ohos_ndk_copy(target_name) {
    forward_variables_from(invoker, "*")
  }
}

# Specify ndk prebuilt library
#
# Input variables:
#   dest_dir: Root directory where sources are copied to.
#   sources: List of files and directories to copy to ${dest_dir}.
#
template("ohos_ndk_prebuilt_library") {
  if (defined(invoker.dest_dir)) {
    _dest_dir = invoker.dest_dir
  } else {
    _dest_dir = "$ndk_libraries_out_dir"
  }

  if (defined(invoker.current_ndk_outpath) &&
      invoker.current_ndk_outpath != "" && use_current_sdk) {
    _dest_dir = invoker.current_ndk_outpath
  }

  ohos_ndk_copy(target_name) {
    forward_variables_from(invoker,
                           "*",
                           [
                             "args",
                             "dest_dir",
                           ])
    dest_dir = _dest_dir

    args = [ "--ignore-stale" ]
    if (defined(invoker.args)) {
      args += invoker.args
    }
  }
}

template("current_ndk") {
  forward_variables_from(invoker,
                         [
                           "ndk_class",
                           "all_ndk_targets",
                           "ndk_out_dir",
                         ])

  package_info_name = "oh-uni-package"
  if (ndk_class != "base") {
    package_info_name = "uni-package"
  }
  package_info_file = "$ndk_out_dir/$package_info_name.json"

  package_info = {
    path = "native"
    displayName = "Native"
    version = current_ndk_version
    if (release_type != "") {
      releaseType = release_type
    }
    if (meta_version != "") {
      meta = {
        metaVersion = meta_version
      }
    }
    if (defined(ext_ndk_config_file) && ext_ndk_config_file != "") {
      platformVersion = platform_version
    }
    apiVersion = api_version
    if (point_split) {
      platformVersion = api_full_version
    }
  }
  write_file(package_info_file, package_info, "json")

  action_with_pydeps(target_name) {
    deps = all_ndk_targets
    script = "//build/ohos/ndk/collect_ndk_syscap.py"
    depfile = "$target_gen_dir/$target_name.d"
    _ndk_syscap_desc_file = "${ndk_out_dir}/ndk_system_capability.json"
    _native_syscap_config_file = "${ndk_out_dir}/nativeapi_syscap_config.json"
    outputs = [ _ndk_syscap_desc_file ]
    args = [
      "--depfile",
      rebase_path(depfile, root_build_dir),
      "--system-capability-file",
      rebase_path(_ndk_syscap_desc_file, root_build_dir),
      "--system-capability-header-config",
      rebase_path(_native_syscap_config_file, root_build_dir),
      "--targets-build-config",
    ]
    foreach(_ndk_target, all_ndk_targets) {
      _target_bc_file = get_label_info(_ndk_target, "target_gen_dir") + "/" +
                        get_label_info(_ndk_target, "name") + ".build_config"
      args += [ rebase_path(_target_bc_file, root_build_dir) ]
    }
  }
}