# Copyright 2016 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# TODO(jmadill): Redo the file entirely when we have standalone GN ANGLE.

third_party_dir = "../../../../third_party"
glslang_dir = "$third_party_dir/glslang-angle/src"
spirv_headers_dir = "$third_party_dir/spirv-headers/src"
spirv_tools_dir = "$third_party_dir/spirv-tools-angle/src"
vulkan_layers_dir = "$third_party_dir/vulkan-validation-layers/src"
raw_vulkan_layers_dir = rebase_path(vulkan_layers_dir)

vulkan_gypi =
    exec_script("//build/gypi_to_gn.py",
                [
                  rebase_path("vulkan.gypi"),
                  "--replace=<(angle_gen_path)=$target_gen_dir/angle",
                  "--replace=<(glslang_path)=../$glslang_dir",
                  "--replace=<(spirv_headers_path)=../$spirv_headers_dir",
                  "--replace=<(spirv_tools_path)=../$spirv_tools_dir",
                  "--replace=<(vulkan_layers_path)=../$vulkan_layers_dir",
                ],
                "scope",
                [ "vulkan.gypi" ])

# Subdirectory to place data files (e.g. layer JSON files).
data_dir = "angledata"

vulkan_gen_json_files_outputs = [
  "$root_out_dir/$data_dir/VkLayer_core_validation.json",
  "$root_out_dir/$data_dir/VkLayer_image.json",
  "$root_out_dir/$data_dir/VkLayer_object_tracker.json",
  "$root_out_dir/$data_dir/VkLayer_parameter_validation.json",
  "$root_out_dir/$data_dir/VkLayer_swapchain.json",
  "$root_out_dir/$data_dir/VkLayer_threading.json",
  "$root_out_dir/$data_dir/VkLayer_unique_objects.json",
]

# Vulkan loader
# -------------

vulkan_gen_dir = "$target_gen_dir/angle/vulkan"
raw_vulkan_gen_dir = rebase_path(vulkan_gen_dir)

config("vulkan_loader_config") {
  include_dirs = rebase_path(vulkan_gypi.vulkan_loader_include_dirs, ".", "src")
  defines = [
    "LAYERS_SOURCE_PATH=\"$data_dir\"",
    "DEFAULT_VK_LAYERS_PATH=\".\"",
    "API_NAME=\"Vulkan\"",
  ]
  if (is_win) {
    defines += [ "VK_USE_PLATFORM_WIN32_KHR" ]
  }
}

config("vulkan_loader_internal_config") {
  if (is_clang || !is_win) {
    cflags = [ "-Wno-unused-function" ]
  }
}

vulkan_undefine_configs = []
if (is_win) {
  vulkan_undefine_configs += [
    "//build/config/win:nominmax",
    "//build/config/win:unicode",
  ]
}

static_library("vulkan_loader") {
  sources = rebase_path(vulkan_gypi.vulkan_loader_sources, ".", "src")
  if (is_win) {
    sources += rebase_path(vulkan_gypi.vulkan_loader_win_sources, ".", "src")
    if (!is_clang) {
      cflags = vulkan_gypi.vulkan_loader_cflags_win
    }
  }
  configs += [ ":vulkan_loader_internal_config" ]
  public_configs = [ ":vulkan_loader_config" ]
  configs -= vulkan_undefine_configs
}

# Vulkan layer helpers
# --------------------

source_set("vulkan_layer_utils") {
  sources = rebase_path(vulkan_gypi.vulkan_layer_utils_sources, ".", "src")
  public_configs = [
    ":vulkan_loader_config",
    ":vulkan_loader_internal_config",
  ]
  configs -= vulkan_undefine_configs
}

helper_script_and_deps = [
  [
    "vulkan_gen_dispatch_table_helper",
    "vk_dispatch_table_helper.h",
    "dispatch_table_generator.py",
  ],
  [
    "vulkan_gen_enum_string_helper",
    "vk_enum_string_helper.h",
    "helper_file_generator.py",
  ],
  [
    "vulkan_gen_safe_struct_cpp",
    "vk_safe_struct.cpp",
    "helper_file_generator.py",
  ],
  [
    "vulkan_gen_safe_struct_h",
    "vk_safe_struct.h",
    "helper_file_generator.py",
  ],
  [
    "vulkan_gen_struct_size_helper_c",
    "vk_struct_size_helper.c",
    "helper_file_generator.py",
  ],
  [
    "vulkan_gen_struct_size_helper_h",
    "vk_struct_size_helper.h",
    "helper_file_generator.py",
  ],
  [
    "vulkan_gen_thread_check_helper",
    "thread_check.h",
    "threading_generator.py",
  ],
  [
    "vulkan_gen_parameter_validation_helper",
    "parameter_validation.h",
    "parameter_validation_generator.py",
  ],
  [
    "vulkan_gen_unique_objects_wrappers_helper",
    "unique_objects_wrappers.h",
    "unique_objects_generator.py",
  ],
]

foreach(script_and_dep, helper_script_and_deps) {
  target_name = script_and_dep[0]
  file = script_and_dep[1]
  dep = script_and_dep[2]
  target("action", target_name) {
    script = "$vulkan_layers_dir/scripts/lvl_genvk.py"
    inputs = [
      "$vulkan_layers_dir/scripts/$dep",
      "$vulkan_layers_dir/scripts/generator.py",
      "$vulkan_layers_dir/scripts/reg.py",
      "$vulkan_layers_dir/scripts/vk.xml",
    ]
    outputs = [
      "$vulkan_gen_dir/$file",
    ]
    args = [
      "-o",
      raw_vulkan_gen_dir,
      "-registry",
      "$raw_vulkan_layers_dir/scripts/vk.xml",
      "$file",
      "-quiet",
    ]
  }
}

config("vulkan_layer_config") {
  include_dirs = [
    vulkan_gen_dir,
    "$vulkan_layers_dir/layers",
  ]
}

source_set("vulkan_layer_table") {
  configs -= vulkan_undefine_configs
  public_deps = [
    ":vulkan_layer_utils",
  ]
  foreach(script_and_dep, helper_script_and_deps) {
    target_name = script_and_dep[0]
    public_deps += [ ":$target_name" ]
  }
  public_configs = [ ":vulkan_layer_config" ]
  sources = [
    "$target_gen_dir/angle/vulkan/vk_dispatch_table_helper.h",
    "$target_gen_dir/angle/vulkan/vk_enum_string_helper.h",
    "$vulkan_layers_dir/layers/vk_layer_table.cpp",
    "$vulkan_layers_dir/layers/vk_layer_table.h",
  ]
}

layer_names = [
  "core_validation",
  "image",
  "object_tracker",
  "swapchain",
  "threading",
  "unique_objects",
  "parameter_validation",
]

action("vulkan_gen_json_files") {
  script = "$third_party_dir/angle/scripts/generate_vulkan_layers_json.py"
  sources =
      rebase_path(vulkan_gypi.vulkan_gen_json_files_sources_win, ".", "src")

  # The layer JSON files are part of the necessary data deps.
  outputs = vulkan_gen_json_files_outputs
  data = vulkan_gen_json_files_outputs
  args = [
    "$raw_vulkan_layers_dir/layers/windows",
    rebase_path("$root_out_dir/$data_dir"),
  ]
}

# SPIRV-tools
# -----------

spirv_source_dir = "$spirv_tools_dir/source"
spirv_include_dir = "$spirv_headers_dir/include/spirv"
raw_spirv_source_dir = rebase_path(spirv_source_dir)
raw_spirv_include_dir = rebase_path(spirv_include_dir)
raw_spirv_headers_dir = rebase_path(spirv_headers_dir)

action("spirv_tools_gen_tables_1_0") {
  script = "$spirv_tools_dir/utils/generate_grammar_tables.py"
  sources = [
    "$spirv_include_dir/1.0/extinst.glsl.std.450.grammar.json",
    "$spirv_include_dir/1.0/spirv.core.grammar.json",
    "$spirv_source_dir/extinst-1.0.opencl.std.grammar.json",
  ]
  outputs = [
    "$vulkan_gen_dir/core.insts-1.0.inc",
    "$vulkan_gen_dir/glsl.std.450.insts-1.0.inc",
    "$vulkan_gen_dir/opencl.std.insts-1.0.inc",
    "$vulkan_gen_dir/operand.kinds-1.0.inc",
  ]
  args = [
    "--spirv-core-grammar=$raw_spirv_include_dir/1.0/spirv.core.grammar.json",
    "--extinst-glsl-grammar=$raw_spirv_include_dir/1.0/extinst.glsl.std.450.grammar.json",
    "--extinst-opencl-grammar=$raw_spirv_source_dir/extinst-1.0.opencl.std.grammar.json",
    "--core-insts-output=$raw_vulkan_gen_dir/core.insts-1.0.inc",
    "--glsl-insts-output=$raw_vulkan_gen_dir/glsl.std.450.insts-1.0.inc",
    "--opencl-insts-output=$raw_vulkan_gen_dir/opencl.std.insts-1.0.inc",
    "--operand-kinds-output=$raw_vulkan_gen_dir/operand.kinds-1.0.inc",
  ]
}

action("spirv_tools_gen_tables_1_1") {
  script = "$spirv_tools_dir/utils/generate_grammar_tables.py"
  sources = [
    "$spirv_include_dir/1.1/spirv.core.grammar.json",
  ]
  outputs = [
    "$vulkan_gen_dir/core.insts-1.1.inc",
    "$vulkan_gen_dir/operand.kinds-1.1.inc",
  ]
  args = [
    "--spirv-core-grammar=$raw_spirv_include_dir/1.1/spirv.core.grammar.json",
    "--core-insts-output=$raw_vulkan_gen_dir/core.insts-1.1.inc",
    "--operand-kinds-output=$raw_vulkan_gen_dir/operand.kinds-1.1.inc",
  ]
}

action("spirv_tools_gen_generators_inc") {
  script = "$spirv_tools_dir/utils/generate_registry_tables.py"
  sources = [
    "$spirv_headers_dir/include/spirv/spir-v.xml",
  ]
  outputs = [
    "$vulkan_gen_dir/generators.inc",
  ]
  args = [
    "--xml=$raw_spirv_headers_dir/include/spirv/spir-v.xml",
    "--generator-output=$raw_vulkan_gen_dir/generators.inc",
  ]
}

config("spirv_tools_config") {
  include_dirs = [ "$spirv_tools_dir/include" ]
  if (is_win) {
    cflags = [
      "/wd4706",

      # These are triggered in higher optimization levels. Disable for now until
      # fixes are landed upstream. See https://crbug.com/677837.
      "/wd4701",
      "/wd4703",
    ]
  }
}

static_library("spirv_tools") {
  deps = [
    ":spirv_tools_gen_generators_inc",
    ":spirv_tools_gen_tables_1_0",
    ":spirv_tools_gen_tables_1_1",
  ]
  include_dirs = [
    vulkan_gen_dir,
    "$spirv_headers_dir/include",
    "$spirv_tools_dir/source",
  ]
  sources = rebase_path(vulkan_gypi.spirv_tools_sources, ".", "src")
  public_configs = [ ":spirv_tools_config" ]
}

# glslang
# -------

config("glslang_config") {
  include_dirs = [
    glslang_dir,
    "$glslang_dir/glslang/Public",
  ]
}

config("glslang_internal_config") {
  if (is_clang || !is_win) {
    cflags = [
      "-Wno-reorder",
      "-Wno-ignored-qualifiers",
    ]
  }
}

static_library("glslang") {
  sources = rebase_path(vulkan_gypi.glslang_sources, ".", "src")
  public_configs = [ ":glslang_config" ]
  configs += [ ":glslang_internal_config" ]

  if (is_win) {
    cflags = [
      "/wd4100",  # Unreferenced formal parameter
      "/wd4456",  # Declaration hides previous local declaration
      "/wd4457",  # Declaration hides function parameter
      "/wd4458",  # Declaration hides class member
      "/wd4702",  # Unreachable code (from glslang_tab.cpp)
      "/wd4718",  # Recursive call has no side effects (from PpContext.cpp)
    ]

    sources += rebase_path(vulkan_gypi.glslang_win_sources, ".", "src")
  }
}

# The validation layers
# ---------------------

config("vulkan_core_validation_config") {
  include_dirs = [ glslang_dir ]
}

source_set("vulkan_core_validation_glslang") {
  public_deps = [
    ":spirv_tools",
  ]
  public_configs = [ ":vulkan_core_validation_config" ]
}

layer_indexes = [
  0,
  1,
  2,
  3,
  4,
  5,
  6,
]

layer_sources = [
  vulkan_gypi.VkLayer_core_validation_sources,
  vulkan_gypi.VkLayer_image_sources,
  vulkan_gypi.VkLayer_swapchain_sources,
  vulkan_gypi.VkLayer_object_tracker_sources,
  vulkan_gypi.VkLayer_unique_objects_sources,
  vulkan_gypi.VkLayer_threading_sources,
  vulkan_gypi.VkLayer_parameter_validation_sources,
]

layer_additional_deps = [
  [ ":vulkan_core_validation_glslang" ],
  [],
  [],
  [],
  [ ":vulkan_gen_unique_objects_wrappers_helper" ],
  [ ":vulkan_gen_thread_check_helper" ],
  [ ":vulkan_gen_parameter_validation_helper" ],
]

foreach(index, layer_indexes) {
  layer = layer_names[index]
  shared_library("VkLayer_$layer") {
    configs -= vulkan_undefine_configs
    deps = [
      ":vulkan_layer_table",
    ]
    deps += layer_additional_deps[index]
    sources = rebase_path(layer_sources[index], ".", "src")
    if (is_win) {
      sources += [ "$vulkan_layers_dir/layers/VKLayer_$layer.def" ]
    }
  }
}

# Use this target to include everything ANGLE needs for Vulkan.
group("angle_vulkan") {
  deps = [
    ":glslang",
    ":vulkan_loader",
  ]
  data_deps = [
    ":vulkan_gen_json_files",
  ]
  foreach(layer, layer_names) {
    data_deps += [ ":VkLayer_$layer" ]
  }
  public_configs = [
    ":vulkan_loader_config",
    ":glslang_config",
  ]
}
