# Copyright 2018 the Deno authors. All rights reserved. MIT license.
import("//build/toolchain/cc_wrapper.gni")
import("//third_party/v8/gni/v8.gni")
import("//third_party/v8/snapshot_toolchain.gni")
import("//build_extra/flatbuffers/flatbuffer.gni")
import("//build_extra/flatbuffers/rust/rust_flatbuffer.gni")
import("//build_extra/deno.gni")
import("//build_extra/rust/rust.gni")
import("//build_extra/toolchain/validate.gni")

group("default") {
  testonly = true
  deps = [
    ":deno",
    ":deno_ns",
    ":test_cc",
    ":test_rs",
  ]
}

config("deno_config") {
  include_dirs = [ "third_party/v8" ]  # This allows us to v8/src/base/ libraries.
  configs = [ "third_party/v8:external_config" ]
  if (is_debug) {
    defines = [ "DEBUG" ]
  }

  # Targets built with the `rust_executable()` template automatically pick up
  # these dependencies, but those built with `executable()` need them when they
  # have Rust inputs. Currently, there's only one such target, `test_cc`.
  if (is_mac) {
    libs = [ "resolv" ]
  }
  if (is_win) {
    libs = [ "userenv.lib" ]
  }

  if (is_clang) {
    cflags = [
      "-fcolor-diagnostics",
      "-fansi-escape-codes",
    ]
  }
}

main_extern = [
  "$rust_build:hyper",
  "$rust_build:hyper_rustls",
  "$rust_build:futures",
  "$rust_build:lazy_static",
  "$rust_build:libc",
  "$rust_build:log",
  "$rust_build:ring",
  "$rust_build:tempfile",
  "$rust_build:rand",
  "$rust_build:tokio",
  "$rust_build:tokio_io",
  "$rust_build:tokio_fs",
  "$rust_build:tokio_executor",
  "$rust_build:tokio_threadpool",
  "$rust_build:url",
  "$rust_build:remove_dir_all",
  "$rust_build:dirs",
  "//build_extra/flatbuffers/rust:flatbuffers",
  ":msg_rs",
]

ts_sources = [
  "js/assets.ts",
  "js/blob.ts",
  "js/compiler.ts",
  "js/console.ts",
  "js/deno.ts",
  "js/dispatch.ts",
  "js/dom_types.ts",
  "js/errors.ts",
  "js/fetch.ts",
  "js/files.ts",
  "js/io.ts",
  "js/global-eval.ts",
  "js/globals.ts",
  "js/libdeno.ts",
  "js/main.ts",
  "js/mkdir.ts",
  "js/make_temp_dir.ts",
  "js/mock_builtin.js",
  "js/os.ts",
  "js/platform.ts",
  "js/plugins.d.ts",
  "js/read_file.ts",
  "js/remove.ts",
  "js/rename.ts",
  "js/read_link.ts",
  "js/stat.ts",
  "js/symlink.ts",
  "js/text_encoding.ts",
  "js/timers.ts",
  "js/trace.ts",
  "js/types.ts",
  "js/util.ts",
  "js/v8_source_maps.ts",
  "js/write_file.ts",
  "tsconfig.json",

  # Listing package.json and yarn.lock as sources ensures the bundle is rebuilt
  # when npm packages are added/removed or their contents changes.
  "package.json",
  "third_party/yarn.lock",
]

rust_executable("deno") {
  source_root = "src/main.rs"
  extern = main_extern
  deps = [
    ":libdeno",
  ]
}

# This target is for fast incremental development.
# When modifying the javascript runtime, this target will not go through the
# extra process of building a snapshot and instead load the bundle from disk.
# ns = no snapshot
rust_executable("deno_ns") {
  source_root = "src/main.rs"
  extern = main_extern
  deps = [
    ":libdeno_nosnapshot",
  ]
}

rust_test("test_rs") {
  source_root = "src/main.rs"
  extern = main_extern
  deps = [
    ":libdeno",
  ]
}

v8_executable("test_cc") {
  testonly = true
  sources = [
    "libdeno/test.cc",
  ]
  deps = [
    ":deno_base_test",
    "//testing/gtest:gtest",
  ]
  configs = [ ":deno_config" ]
}

static_library("libdeno") {
  complete_static_lib = true
  sources = [
    "libdeno/from_snapshot.cc",
  ]
  inputs = [
    "$target_gen_dir/snapshot_deno.bin",
  ]
  deps = [
    ":create_snapshot_deno",
    ":deno_bindings",
  ]
  configs += [ ":deno_config" ]

  # from_snapshot.cc uses an assembly '.incbin' directive to embed the snapshot.
  # This causes trouble when using sccache: since the snapshot file is not
  # inlined by the c preprocessor, sccache doesn't take its contents into
  # consideration, leading to false-positive cache hits.
  # Maybe other caching tools have this issue too, but ccache is unaffected.
  # Therefore, if a cc_wrapper is used that isn't ccache, include a generated
  # header file that contains the the sha256 hash of the snapshot.
  if (cc_wrapper != "" && cc_wrapper != "ccache") {
    hash_h = "$target_gen_dir/bundle/hash.h"
    inputs += [ hash_h ]
    deps += [ ":bundle_hash_h" ]
    if (is_win) {
      cflags = [ "/FI" + rebase_path(hash_h, target_out_dir) ]
    } else {
      cflags = [
        "-include",
        rebase_path(hash_h, target_out_dir),
      ]
    }
  }
}

# Only functionality needed for libdeno_test and snapshot_creator
# In particular no flatbuffers, no assets, no rust, no msg handlers.
# Because snapshots are slow, it's important that snapshot_creator's
# dependencies are minimal.
v8_source_set("deno_base") {
  sources = [
    "libdeno/binding.cc",
    "libdeno/deno.h",
    "libdeno/file_util.cc",
    "libdeno/file_util.h",
    "libdeno/internal.h",
  ]
  public_deps = [
    "third_party/v8:v8_monolith",
  ]
  configs = [ ":deno_config" ]
}

v8_source_set("deno_base_test") {
  testonly = true
  sources = [
    "libdeno/file_util_test.cc",
    "libdeno/from_snapshot.cc",
    "libdeno/libdeno_test.cc",
  ]
  inputs = [
    "$target_gen_dir/snapshot_libdeno_test.bin",
  ]
  deps = [
    ":create_snapshot_libdeno_test",
    ":deno_base",
    "//testing/gtest:gtest",
  ]
  defines = [ "LIBDENO_TEST" ]
  configs = [ ":deno_config" ]
}

v8_source_set("deno_bindings") {
  deps = [
    ":deno_base",
  ]
  configs = [ ":deno_config" ]
}

executable("snapshot_creator") {
  sources = [
    "libdeno/snapshot_creator.cc",
  ]
  deps = [
    ":deno_base",
  ]
  configs += [ ":deno_config" ]
}

# Generates type declarations for files that need to be included
# in the runtime bundle
run_node("gen_declarations") {
  out_dir = target_gen_dir
  sources = ts_sources
  outputs = [
    "$out_dir/types/globals.d.ts",
  ]
  deps = [
    ":msg_ts",
  ]
  args = [
    "./node_modules/typescript/bin/tsc",
    "-p",
    rebase_path("js/tsconfig.generated.json", root_build_dir),
    "--baseUrl",
    rebase_path(root_build_dir, root_build_dir),
    "--outFile",
    rebase_path("$out_dir/types/globals.js", root_build_dir),
  ]
}

run_node("bundle") {
  out_dir = "$target_gen_dir/bundle/"
  sources = ts_sources + [ "rollup.config.js" ]
  outputs = [
    out_dir + "main.js",
    out_dir + "main.js.map",
  ]
  deps = [
    ":gen_declarations",
    ":msg_ts",
  ]
  args = [
    "./node_modules/rollup/bin/rollup",
    "-c",
    rebase_path("rollup.config.js", root_build_dir),
    "-i",
    rebase_path("js/main.ts", root_build_dir),
    "-o",
    rebase_path(out_dir + "main.js", root_build_dir),
    "--sourcemapFile",
    rebase_path("."),
    "--silent",
    "--environment",
    "BASEPATH:" + rebase_path(".", root_build_dir),
  ]
}

action("bundle_hash_h") {
  script = "//tools/sha256sum.py"
  inputs = get_target_outputs(":bundle")
  outputs = [
    "$target_gen_dir/bundle/hash.h",
  ]
  deps = [
    ":bundle",
  ]
  args = [
    "--format",
    "__attribute__((__unused__)) static const int dummy_%s = 0;",
    "--outfile",
    rebase_path(outputs[0], root_build_dir),
  ]
  foreach(input, inputs) {
    args += [
      "--infile",
      rebase_path(input, root_build_dir),
    ]
  }
}

source_set("libdeno_nosnapshot") {
  bundle_outputs = get_target_outputs(":bundle")
  bundle_location = rebase_path(bundle_outputs[0], root_build_dir)
  bundle_map_location = rebase_path(bundle_outputs[1], root_build_dir)
  inputs = bundle_outputs
  sources = [
    "libdeno/from_filesystem.cc",
  ]
  deps = [
    ":bundle",
    ":deno_bindings",
  ]
  configs += [ ":deno_config" ]
  defines = [
    "BUNDLE_LOCATION=\"$bundle_location\"",
    "BUNDLE_MAP_LOCATION=\"$bundle_map_location\"",
  ]
}

ts_flatbuffer("msg_ts") {
  sources = [
    "src/msg.fbs",
  ]
}

rust_flatbuffer("msg_rs") {
  sources = [
    "src/msg.fbs",
  ]
}

# Generates $target_gen_dir/snapshot_deno.bin
create_snapshot("deno") {
  js = "$target_gen_dir/bundle/main.js"
  source_map = "$target_gen_dir/bundle/main.js.map"
  deps = [
    ":bundle",
  ]
}

# Generates $target_gen_dir/snapshot_libdeno_test.bin
create_snapshot("libdeno_test") {
  testonly = true
  js = "libdeno/libdeno_test.js"
}