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

import("//flutter/build/zip_bundle.gni")
import("//flutter/common/config.gni")
import("//flutter/lib/web_ui/flutter_js/sources.gni")
import("//flutter/web_sdk/web_sdk.gni")
import("$dart_src/build/dart/dart_action.gni")

web_ui_sources = exec_script("$dart_src/tools/list_dart_files.py",
                             [
                               "absolute",
                               rebase_path("//flutter/lib/web_ui/lib"),
                             ],
                             "list lines")

web_engine_libraries = [
  ":skwasm_impl_library",
  ":skwasm_stub_library",
  ":web_engine_library",
  ":web_ui_library",
  ":web_ui_ui_web",
  ":web_ui_library_sources",
  ":web_test_fonts_library",
  ":web_locale_keymap_library",
]

group("web_sdk") {
  deps = [
    ":flutter_ddc_modules",
    ":flutter_platform_dills",
    "//flutter/lib/web_ui/flutter_js",
  ]
}

sdk_rewriter("web_ui_library_sources") {
  ui = true
  input_dir = "//flutter/lib/web_ui/lib/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/ui/"

  # exclude everything in the engine directory, it will be a separate internal library
  exclude_pattern = rebase_path("//flutter/lib/web_ui/lib/src")
}

web_ui_ui_web_with_output("web_ui_ui_web") {
  output_dir = "$root_out_dir/flutter_web_sdk/lib/ui_web/"
}

sdk_rewriter("web_engine_library") {
  library_name = "engine"
  api_file = "//flutter/lib/web_ui/lib/src/engine.dart"
  input_dir = "//flutter/lib/web_ui/lib/src/engine/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_engine/"

  # exclude skwasm, it will be a separate internal library
  exclude_pattern = rebase_path("//flutter/lib/web_ui/lib/src/engine/skwasm")
}

sdk_rewriter("skwasm_stub_library") {
  library_name = "skwasm_stub"
  api_file = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart"
  input_dir = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_skwasm_stub/"
}

sdk_rewriter("skwasm_impl_library") {
  library_name = "skwasm_impl"
  api_file = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart"
  input_dir = "//flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_skwasm_impl/"
}

sdk_rewriter("web_test_fonts_library") {
  library_name = "web_test_fonts"
  api_file = "//flutter/third_party/web_test_fonts/lib/web_test_fonts.dart"
  input_dir = "//flutter/third_party/web_test_fonts/lib/web_test_fonts/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_web_test_fonts/"
}

sdk_rewriter("web_locale_keymap_library") {
  library_name = "web_locale_keymap"
  api_file =
      "//flutter/third_party/web_locale_keymap/lib/web_locale_keymap.dart"
  input_dir = "//flutter/third_party/web_locale_keymap/lib/web_locale_keymap/"
  output_dir = "$root_out_dir/flutter_web_sdk/lib/_web_locale_keymap/"
}

copy("web_ui_library") {
  sources = [ "//flutter/web_sdk/libraries.json" ]

  outputs = [ "$root_out_dir/flutter_web_sdk/{{source_file_part}}" ]
}

# Compiles a Dart program with dartdevc
#
# Parameters:
#
#   inputs (required): The inputs to dartdevc
#
#   outputs (required): The files generated by dartdevc
#
#   args (required): The arguments to pass to dartdevc
template("_dartdevc") {
  if (flutter_prebuilt_dart_sdk) {
    action(target_name) {
      not_needed(invoker, [ "packages" ])
      deps = web_engine_libraries
      script = "//build/gn_run_binary.py"

      inputs = invoker.inputs
      outputs = invoker.outputs

      pool = "//build/toolchain:toolchain_pool"

      ext = ""
      if (is_win) {
        ext = ".exe"
      }
      dartaot = rebase_path("$host_prebuilt_dart_sdk/bin/dartaotruntime$ext",
                            root_out_dir)
      dartdevc = rebase_path(
              "$host_prebuilt_dart_sdk/bin/snapshots/dartdevc_aot.dart.snapshot")
      args = [
               dartaot,
               dartdevc,
             ] + invoker.args
    }
  } else {
    prebuilt_dart_action(target_name) {
      forward_variables_from(invoker, "*")

      deps = web_engine_libraries + [
               "$dart_src:create_sdk",
               "$dart_src/pkg:pkg_files_stamp",
               "$dart_src/utils/ddc:ddc_files_stamp",
               "$dart_src/utils/ddc:ddc_sdk_patch_stamp",
             ]

      script = "$dart_src/pkg/dev_compiler/bin/dartdevc.dart"

      pool = "//build/toolchain:toolchain_pool"
    }
  }
}

# Compiles a Dart SDK with the kernel_worker
#
# Parameters:
#
#   inputs (required): The inputs to the kernel_worker
#
#   outputs (required): The files generated by the kernel_worker
#
#   args (required): The arguments to pass to the kernel_worker
template("_kernel_worker") {
  if (flutter_prebuilt_dart_sdk) {
    action(target_name) {
      deps = web_engine_libraries
      script = "//build/gn_run_binary.py"

      inputs = invoker.inputs
      outputs = invoker.outputs

      pool = "//build/toolchain:toolchain_pool"

      ext = ""
      if (is_win) {
        ext = ".exe"
      }
      dartaot = rebase_path("$host_prebuilt_dart_sdk/bin/dartaotruntime$ext",
                            root_out_dir)
      kernel_worker = rebase_path(
              "$host_prebuilt_dart_sdk/bin/snapshots/kernel_worker_aot.dart.snapshot")

      args = [
               dartaot,
               kernel_worker,
             ] + invoker.args
    }
  } else {
    prebuilt_dart_action(target_name) {
      forward_variables_from(invoker, "*")

      deps = web_engine_libraries + [
               "$dart_src:create_sdk",
               "$dart_src/pkg:pkg_files_stamp",
               "$dart_src/utils/ddc:ddc_files_stamp",
               "$dart_src/utils/ddc:ddc_sdk_patch_stamp",
             ]

      script = "$dart_src/utils/bazel/kernel_worker.dart"

      pool = "//build/toolchain:toolchain_pool"
    }
  }
}

template("_compile_platform") {
  assert(defined(invoker.kernel_target),
         "kernel_target must be defined for $target_name")
  assert(defined(invoker.summary_only),
         "summary_only must be defined for $target_name")
  assert(defined(invoker.output_dill),
         "output_dill must be defined for $target_name")
  _kernel_worker(target_name) {
    inputs = [ "sdk_rewriter.dart" ] + web_ui_sources

    outputs = [ invoker.output_dill ]
    args = []

    if (invoker.summary_only) {
      args += [ "--summary-only" ]
    } else {
      args += [ "--no-summary-only" ]
    }
    if (defined(invoker.null_environment) && invoker.null_environment) {
      args += [ "--null-environment" ]
    }
    skwasm_library = "dart:_skwasm_stub"
    if (invoker.kernel_target == "dart2wasm") {
      skwasm_library = "dart:_skwasm_impl"
    } else if (invoker.kernel_target == "ddc") {
      args += [ "--include-unsupported-platform-library-stubs" ]
    }

    args += [
      "--target",
      "${invoker.kernel_target}",
      "--packages-file",
      "file:///" + rebase_path(dart_sdk_package_config),
      "--multi-root-scheme",
      "org-dartlang-sdk",
      "--multi-root",
      "file:///" + rebase_path("$root_out_dir/flutter_web_sdk"),
      "--libraries-file",
      "org-dartlang-sdk:///libraries.json",
      "--output",
      rebase_path(invoker.output_dill),
      "--source",
      "dart:core",

      # Additional Flutter web dart libraries
      "--source",
      "dart:ui",
      "--source",
      "dart:ui_web",
      "--source",
      "dart:_engine",
      "--source",
      skwasm_library,
      "--source",
      "dart:_web_locale_keymap",
    ]
    if (flutter_prebuilt_dart_sdk) {
      args += [
        "--multi-root",
        "file:///" + rebase_path("$host_prebuilt_dart_sdk/.."),
      ]
    } else {
      args += [
        "--multi-root",
        "file:///" + rebase_path("$root_out_dir"),
      ]
    }
  }
}

_compile_platform("flutter_dartdevc_kernel_sdk_outline") {
  kernel_target = "ddc"
  summary_only = true
  output_dill = "$root_out_dir/flutter_web_sdk/kernel/ddc_outline.dill"
}

_compile_platform("flutter_dart2js_kernel_sdk_full") {
  kernel_target = "dart2js"
  summary_only = false
  output_dill = "$root_out_dir/flutter_web_sdk/kernel/dart2js_platform.dill"
  null_environment = true
}

_compile_platform("flutter_dart2wasm_kernel_sdk_full") {
  kernel_target = "dart2wasm"
  summary_only = false
  output_dill = "$root_out_dir/flutter_web_sdk/kernel/dart2wasm_platform.dill"
  null_environment = true
}

group("flutter_platform_dills") {
  public_deps = [
    ":flutter_dart2js_kernel_sdk_full",
    ":flutter_dart2wasm_kernel_sdk_full",
    ":flutter_dartdevc_kernel_sdk_outline",
  ]
}

template("_compile_ddc_module") {
  assert(defined(invoker.sdk_path_prefix),
         "sdk_path_prefix must be defined for $target_name")
  assert(defined(invoker.module_format),
         "module_name must be defined for $target_name")
  assert(defined(invoker.canary), "canary must be defined for $target_name")

  _dartdevc(target_name) {
    inputs = [ "sdk_rewriter.dart" ] + web_ui_sources

    packages = dart_sdk_package_config

    name_suffix = "-canvaskit"

    dart_sdk_js_path = "$root_out_dir/flutter_web_sdk/kernel/${invoker.sdk_path_prefix}${name_suffix}/dart_sdk.js"

    outputs = [
      dart_sdk_js_path,
      dart_sdk_js_path + ".map",
    ]

    args = [
      "--compile-sdk",
      "dart:core",

      # Additional Flutter web dart libraries
      "dart:ui",
      "dart:ui_web",
      "dart:_engine",
      "dart:_skwasm_stub",
      "dart:_web_locale_keymap",
      "--no-summarize",
      "--packages",
      "file:///" + rebase_path(dart_sdk_package_config),
      "--multi-root-scheme",
      "org-dartlang-sdk",
      "--multi-root",
      "file:///" + rebase_path("$root_out_dir/flutter_web_sdk"),
      "--multi-root-output-path",
      rebase_path("$root_out_dir/"),
      "--libraries-file",
      "org-dartlang-sdk:///libraries.json",
      "--inline-source-map",
      "-DFLUTTER_WEB_USE_SKIA=true",
      "--modules",
      invoker.module_format,
      "-o",
      rebase_path(dart_sdk_js_path),
    ]
    if (invoker.canary) {
      args += [ "--canary" ]
    }
    if (flutter_prebuilt_dart_sdk) {
      args += [
        "--multi-root",
        "file:///" + rebase_path("$host_prebuilt_dart_sdk/.."),
      ]
    } else {
      args += [
        "--multi-root",
        "file:///" + rebase_path("$root_out_dir"),
      ]
    }
  }
}

template("_compile_ddc_modules") {
  forward_variables_from(invoker, "*")

  # We compile multiple times instead of passing multiple modules to a single
  # compile as the DDC library bundle format cannot be used when passing
  # multiple module formats as arguments.
  _compile_ddc_module("${target_name}_amd") {
    sdk_path_prefix = "amd"
    module_format = "amd"
    canary = false
  }

  _compile_ddc_module("${target_name}_ddcLibraryBundle") {
    sdk_path_prefix = "ddcLibraryBundle"
    module_format = "ddc"
    canary = true
  }
}

# Compiles the canvaskit only renderer.
_compile_ddc_modules("flutter_dartdevc_canvaskit_kernel_sdk") {
}

group("flutter_ddc_modules") {
  public_deps = [
    ":flutter_dartdevc_canvaskit_kernel_sdk_amd",
    ":flutter_dartdevc_canvaskit_kernel_sdk_ddcLibraryBundle",
  ]
}

# Archives Flutter Web SDK
if (!is_fuchsia) {
  zip_bundle_from_file("flutter_web_sdk_archive") {
    output = "flutter-web-sdk.zip"
    deps = [
             ":flutter_ddc_modules",
             ":flutter_platform_dills",
           ] + web_engine_libraries

    if (is_wasm) {
      deps += [
        "//flutter/third_party/canvaskit:canvaskit_chromium_group",
        "//flutter/third_party/canvaskit:canvaskit_group",
        "//flutter/third_party/canvaskit:skwasm_group",
        "//flutter/third_party/canvaskit:skwasm_heavy_group",
        "//flutter/third_party/canvaskit:wimp_group",
      ]
    }
    deps += [ "//flutter/lib/web_ui/flutter_js" ]

    # flutter_ddc_modules
    sources = get_target_outputs(":flutter_dartdevc_canvaskit_kernel_sdk_amd")
    sources += get_target_outputs(
            ":flutter_dartdevc_canvaskit_kernel_sdk_ddcLibraryBundle")

    # flutter_platform_dills
    sources += get_target_outputs(":flutter_dartdevc_kernel_sdk_outline")
    sources += get_target_outputs(":flutter_dart2js_kernel_sdk_full")
    sources += get_target_outputs(":flutter_dart2wasm_kernel_sdk_full")
    if (is_wasm) {
      sources += [
        "$root_out_dir/flutter_web_sdk/canvaskit/canvaskit.js",
        "$root_out_dir/flutter_web_sdk/canvaskit/canvaskit.js.symbols",
        "$root_out_dir/flutter_web_sdk/canvaskit/canvaskit.wasm",
        "$root_out_dir/flutter_web_sdk/canvaskit/chromium/canvaskit.js",
        "$root_out_dir/flutter_web_sdk/canvaskit/chromium/canvaskit.js.symbols",
        "$root_out_dir/flutter_web_sdk/canvaskit/chromium/canvaskit.wasm",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.js",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.js.symbols",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm.wasm",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm_heavy.js",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm_heavy.js.symbols",
        "$root_out_dir/flutter_web_sdk/canvaskit/skwasm_heavy.wasm",
        "$root_out_dir/flutter_web_sdk/canvaskit/wimp.js",
        "$root_out_dir/flutter_web_sdk/canvaskit/wimp.js.symbols",
        "$root_out_dir/flutter_web_sdk/canvaskit/wimp.wasm",
      ]
    }

    # flutter.js full sources
    foreach(source, flutter_js_source_list) {
      sources += [ "$root_out_dir/flutter_web_sdk/flutter_js/$source" ]
    }

    # flutter.js bundled source and sourcemap
    sources += [
      "$root_out_dir/flutter_web_sdk/flutter_js/flutter.js",
      "$root_out_dir/flutter_web_sdk/flutter_js/flutter.js.map",
    ]

    foreach(web_engine_library, web_engine_libraries) {
      sources += get_target_outputs(web_engine_library)
    }

    tmp_files = []
    web_sdk_files =
        filter_include(sources, [ "$root_build_dir/flutter_web_sdk/**" ])
    foreach(source, web_sdk_files) {
      tmp_files += [
        {
          source = rebase_path(source)
          destination = rebase_path(source, "$root_build_dir/flutter_web_sdk")
        },
      ]
    }
    files = tmp_files
  }
}
