# Copyright (c) 2013 The Chromium 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("//testing/libfuzzer/fuzzer_test.gni")

declare_args() {
  # Controls whether the build should uses the version of sqlite3 library
  # shipped with the system (currently only supported on iOS) or the one
  # shipped with Chromium source.
  use_system_sqlite = is_ios
}

if (!use_system_sqlite) {
  config("sqlite_warnings") {
    cflags = []
    if (is_clang) {
      # sqlite contains a few functions that are unused, at least on
      # Windows with Chromium's sqlite patches applied
      # (interiorCursorEOF fts3EvalDeferredPhrase
      # fts3EvalSelectDeferred sqlite3Fts3InitHashTable
      # sqlite3Fts3InitTok).
      cflags += [ "-Wno-unused-function" ]
    }
    if (is_linux) {
      cflags += [
        # SQLite doesn"t believe in compiler warnings,
        # preferring testing.
        #   http://www.sqlite.org/faq.html#q17
        "-Wno-int-to-pointer-cast",
        "-Wno-pointer-to-int-cast",
      ]
    }
  }

  # "sqlite3" can cause conflicts with the system library.
  component("chromium_sqlite3") {
    visibility = [ ":*" ]
    sources = [
      "amalgamation/config.h",
      "amalgamation/sqlite3.c",
      "amalgamation/sqlite3.h",
      "src/src/recover.c",
      "src/src/recover.h",
      "src/src/recover_varint.c",
    ]

    cflags = []
    defines = [
      "SQLITE_ENABLE_FTS3",

      # New unicode61 tokenizer with built-in tables.
      "SQLITE_DISABLE_FTS3_UNICODE",

      # Chromium currently does not enable fts4, disable extra code.
      "SQLITE_DISABLE_FTS4_DEFERRED",
      "SQLITE_ENABLE_ICU",
      "SQLITE_ENABLE_MEMORY_MANAGEMENT",
      "SQLITE_SECURE_DELETE",

      # Custom flag to tweak pcache pools.
      # TODO(shess): This shouldn't use faux-SQLite naming.
      "SQLITE_SEPARATE_CACHE_POOLS",

      # TODO(shess): SQLite adds mutexes to protect structures which cross
      # threads.  In theory Chromium should be able to turn this off for a
      # slight speed boost.
      "THREADSAFE",

      # SQLite can spawn threads to sort in parallel if configured
      # appropriately.  Chromium doesn't configure SQLite for that, and would
      # prefer to control distribution to worker threads.
      "SQLITE_MAX_WORKER_THREADS=0",

      # Allow 256MB mmap footprint per connection.  Should not be too open-ended
      # as that could cause memory fragmentation.  50MB encompasses the 99th
      # percentile of Chrome databases in the wild.
      # TODO(shess): A 64-bit-specific value could be 1G or more.
      # TODO(shess): Figure out if exceeding this is costly.
      "SQLITE_MAX_MMAP_SIZE=268435456",

      # Use a read-only memory map when mmap'ed I/O is enabled to prevent memory
      # stompers from directly corrupting the database.
      # TODO(shess): Upstream the ability to use this define.
      "SQLITE_MMAP_READ_ONLY=1",

      # By default SQLite pre-allocates 100 pages of pcache data, which will not
      # be released until the handle is closed.  This is contrary to Chromium's
      # memory-usage goals.
      "SQLITE_DEFAULT_PCACHE_INITSZ=0",

      # NOTE(shess): Some defines can affect the amalgamation.  Those should be
      # added to google_generate_amalgamation.sh, and the amalgamation
      # re-generated.  Usually this involves disabling features which include
      # keywords or syntax, for instance SQLITE_OMIT_VIRTUALTABLE omits the
      # virtual table syntax entirely.  Missing an item usually results in
      # syntax working but execution failing.  Review:
      #   src/src/parse.py
      #   src/tool/mkkeywordhash.c
    ]
    if (is_component_build) {
      if (is_win) {
        defines += [ "SQLITE_API=__declspec(dllexport)" ]
      } else {
        defines += [ "SQLITE_API=__attribute__((visibility(\"default\")))" ]
      }
    }
    if (is_posix) {
      defines += [
        # Allow xSleep() call on Unix to use usleep() rather than sleep(), so it
        # will have microsecond precision.  Should only affect contended
        # databases via the busy callback.  Browser profile databases are mostly
        # exclusive, but renderer databases may allow for contention.
        "HAVE_USLEEP=1",

        # Use pread/pwrite directly rather than emulating them.
        "USE_PREAD=1",
      ]
    }
    if (is_linux || is_android) {
      defines += [
        # Linux provides fdatasync(), a faster equivalent of fsync().
        "fdatasync=fdatasync",
      ]
    }

    # Pull in config.h on Linux.  This allows use of preprocessor macros which
    # are not available to the build config.
    if (is_linux) {
      defines += [ "_HAVE_SQLITE_CONFIG_H" ]
    }

    if (using_sanitizer) {
      # Limit max length of data blobs and queries for fuzzing builds by 128 MB.
      defines += [
        "SQLITE_MAX_LENGTH=128000000",
        "SQLITE_MAX_SQL_LENGTH=128000000",
        "SQLITE_PRINTF_PRECISION_LIMIT=128000000",
      ]
    }

    include_dirs = [ "amalgamation" ]

    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [
      "//build/config/compiler:no_chromium_code",

      # Must be after no_chromium_code for warning flags to be ordered
      # correctly.
      ":sqlite_warnings",
    ]

    if (is_linux) {
      libs = [ "dl" ]
    } else if (is_mac || is_ios) {
      libs = [
        "CoreFoundation.framework",
        "CoreServices.framework",
      ]
    } else if (is_android) {
      defines += [
        "SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576",
        "SQLITE_DEFAULT_AUTOVACUUM=1",
        "SQLITE_TEMP_STORE=3",
        "SQLITE_ENABLE_FTS3_BACKWARDS",
        "DSQLITE_DEFAULT_FILE_FORMAT=4",
      ]
    }

    deps = [
      "//third_party/icu",
    ]
  }

  config("sqlite_export") {
    if (is_component_build && is_win) {
      defines = [ "SQLITE_API=__declspec(dllimport)" ]
    }
  }

  # This is used to allow the SQLITE_API definition to be different when
  # building sqlite3.c than it is when clients include sqlite3.h.
  group("sqlite") {
    public_deps = [
      ":chromium_sqlite3",
    ]
    public_configs = [ ":sqlite_export" ]
  }

  if (is_linux) {
    executable("sqlite_shell") {
      # So shell.c can find the correct sqlite3.h.
      include_dirs = [ "amalgamation" ]

      sources = [
        "src/src/shell.c",
        "src/src/shell_icu_linux.c",

        # Include a dummy c++ file to force linking of libstdc++.
        "build_as_cpp.cc",
      ]

      deps = [
        ":sqlite",
        "//build/config/sanitizers:deps",
        "//third_party/icu",
      ]
    }
  }
}

if (use_system_sqlite) {
  # iOS uses the version of sqlite3 shipped with the system instead of the
  # version shipped with Chromium. Export a "sqlite" target so the change
  # can be localized to this file.

  config("sqlite_config") {
    defines = [ "USE_SYSTEM_SQLITE" ]
    if (is_ios) {
      libs = [ "sqlite3" ]
    } else {
      assert(false, "extra flags to use system sqlite3 library missing")
    }
  }

  source_set("sqlite") {
    public_configs = [ ":sqlite_config" ]
    if (is_ios) {
      public_deps = [
        ":sqlite_recover",
      ]
      deps = [
        ":sqlite_regexp",
      ]
    }
  }

  if (is_ios) {
    source_set("sqlite_recover") {
      sources = [
        # TODO(shess): Move out of the SQLite source tree, perhaps to ext/.
        "src/src/recover.c",
        "src/src/recover.h",
        "src/src/recover_varint.c",
      ]
    }

    source_set("sqlite_regexp") {
      defines = [
        # Necessary to statically compile the extension.
        "SQLITE_CORE",
        "SQLITE_ENABLE_ICU",
        "SQLITE_ENABLE_MEMORY_MANAGEMENT",
      ]
      sources = [
        "src/ext/icu/icu.c",
      ]
      deps = [
        "//third_party/icu",
      ]
      if (is_clang) {
        # src/ext/icu/icu.c uses assert(!"string") which causes warnings about
        # conversion from string literal to bool.
        configs -= [ "//build/config/clang:extra_warnings" ]
      }
    }
  }
}

fuzzer_test("sqlite3_prepare_v2_fuzzer") {
  sources = [
    "fuzz/sqlite3_prepare_v2_fuzzer.cc",
  ]
  deps = [
    ":sqlite",
  ]
  dict = "fuzz/sqlite3_prepare_v2_fuzzer.dict"
}
