# Copyright 2015 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("//build/config/sanitizers/sanitizers.gni")
import("//build/toolchain/goma.gni")

declare_args() {
  # The default number of jobs to use when building instrumented libraries.
  instrumented_libraries_jobs = 8

  # The platform on which the instrumented libraries are being built.
  instrumented_libraries_platform = "trusty"
}

group("deps") {
  if (use_prebuilt_instrumented_libraries) {
    assert(prebuilt_instrumented_libraries_available,
           "Prebuilt instrumented libraries are only available when " +
               "is_msan = true and msan_track_origins = {0, 2}")
    deps = [
      ":prebuilt",
    ]
    data_deps = [
      ":prebuilt",
    ]
  }
  if (use_locally_built_instrumented_libraries) {
    deps = [
      ":locally_built",
    ]
  }
}

if (is_msan) {
  sanitizer_type = "msan"
} else if (is_asan) {
  sanitizer_type = "asan"
} else if (is_tsan) {
  sanitizer_type = "tsan"
}

if (prebuilt_instrumented_libraries_available) {
  group("prebuilt") {
    visibility = [ ":deps" ]

    deps = [
      ":extract_prebuilt_instrumented_libraries",
    ]
    data_deps = [
      ":extract_prebuilt_instrumented_libraries",
    ]
  }

  if (is_msan) {
    if (msan_track_origins == 0) {
      archive_prefix = "msan-no-origins"
    } else if (msan_track_origins == 2) {
      archive_prefix = "msan-chained-origins"
    }
  }

  # TODO(GYP): scripts/download_binaries.py uses GYP_DEFINES to decide whether
  # to download the archives extracted here.
  # Note: This requires a clobber whenever Ubuntu version changes.
  action("extract_prebuilt_instrumented_libraries") {
    visibility = [ ":prebuilt" ]
    script = "scripts/unpack_binaries.py"
    depfile = "$target_out_dir/$archive_prefix.d"
    args = [
      archive_prefix,
      rebase_path("binaries"),
      rebase_path(root_out_dir + "/instrumented_libraries_prebuilt"),
      rebase_path(target_out_dir, root_out_dir),
    ]
    outputs = [
      "$target_out_dir/$archive_prefix.txt",
    ]
    data = [
      "$root_out_dir/instrumented_libraries_prebuilt/",
    ]
  }

  config("prebuilt_ldflags") {
    ldflags = [
      # Add a relative RPATH entry to Chromium binaries. This puts instrumented
      # DSOs before system-installed versions in library search path.
      "-Wl,-R,\$ORIGIN/instrumented_libraries_prebuilt/$sanitizer_type/lib",
      "-Wl,-z,origin",
    ]
  }
}

if (use_locally_built_instrumented_libraries) {
  if (instrumented_libraries_platform == "precise") {
    is_precise = true
    is_trusty = false
  } else if (instrumented_libraries_platform == "trusty") {
    is_precise = false
    is_trusty = true
  } else {
    assert(false, "Unsupported platform " + instrumented_libraries_platform)
  }
  group("locally_built") {
    visibility = [ ":deps" ]
    deps = [
      ":atk1.0",
      ":brltty",
      ":dee",
      ":freetype",
      ":libappindicator1",
      ":libasound2",
      ":libcairo2",
      ":libcap2",
      ":libcredentialkit_pkcs11",
      ":libdbus-1-3",
      ":libdbus-glib-1-2",
      ":libdbusmenu",
      ":libdbusmenu-glib4",
      ":libexpat1",
      ":libffi6",
      ":libfontconfig1",
      ":libgconf-2-4",
      ":libgcrypt11",
      ":libgdk-pixbuf2.0-0",
      ":libglib2.0-0",
      ":libgnome-keyring0",
      ":libgpg-error0",
      ":libgtk2.0-0",
      ":libnspr4",
      ":libp11-kit0",
      ":libpci3",
      ":libpcre3",
      ":libpixman-1-0",
      ":libunity9",
      ":libva1",
      ":libx11-6",
      ":libxau6",
      ":libxcb1",
      ":libxcomposite1",
      ":libxcursor1",
      ":libxdamage1",
      ":libxdmcp6",
      ":libxext6",
      ":libxfixes3",
      ":libxi6",
      ":libxinerama1",
      ":libxrandr2",
      ":libxrender1",
      ":libxss1",
      ":libxtst6",
      ":nss",
      ":pango1.0",
      ":pulseaudio",
      ":udev",
      ":zlib1g",
    ]
    if (is_precise) {
      deps += [ ":libtasn1-3" ]
    }
    if (is_trusty) {
      deps += [
        ":harfbuzz",
        ":libsecret",
        ":libtasn1-6",
      ]
    }
    if (is_msan) {
      deps += [ ":libcups2" ]
    }
    if (!is_tsan) {
      deps += [ ":libpng12-0" ]
    }
    data = [
      "${root_out_dir}/instrumented_libraries/${sanitizer_type}/lib",
    ]
  }

  config("locally_built_ldflags") {
    ldflags = [
      # Add a relative RPATH entry to Chromium binaries. This puts instrumented
      # DSOs before system-installed versions in library search path.
      "-Wl,-R,\$ORIGIN/instrumented_libraries/${sanitizer_type}/lib",
      "-Wl,-z,origin",
    ]
  }

  template("instrumented_library") {
    action(target_name) {
      script = "scripts/download_build_install.py"
      build_method = "destdir"
      if (defined(invoker.build_method)) {
        build_method = invoker.build_method
      }

      cc = rebase_path("//third_party/llvm-build/Release+Asserts/bin/clang")
      cxx = rebase_path("//third_party/llvm-build/Release+Asserts/bin/clang++")
      if (use_goma) {
        cc = "${goma_dir}/gomacc ${cc}"
        cxx = "${goma_dir}/gomacc ${cxx}"
      }
      jobs = instrumented_libraries_jobs
      if (defined(invoker.jobs)) {
        jobs = invoker.jobs
      }
      package_cflags = [
        "-O2",
        "-gline-tables-only",
        "-fPIC",
        "-w",
        "-U_FORITFY_SOURCE",
        "-fno-omit-frame-pointer",
      ]
      package_ldflags = []
      if (is_asan) {
        package_cflags += [ "-fsanitize=address" ]
        package_ldflags = [ "-fsanitize=address" ]
      } else if (is_msan) {
        package_cflags += [
          "-fsanitize=memory",
          "-fsanitize-memory-track-origins=${msan_track_origins}",
        ]
        package_ldflags = [ "-fsanitize=memory" ]
      } else if (is_tsan) {
        package_cflags += [ "-fsanitize=thread" ]
        package_ldflags = [ "-fsanitize=thread" ]
      }
      if (defined(invoker.package_cflags)) {
        package_cflags += invoker.package_cflags
      }
      if (defined(invoker.package_ldflags)) {
        package_ldflags += invoker.package_ldflags
      }
      if (defined(invoker.deps)) {
        deps = invoker.deps
      }
      product_dir = rebase_path(root_out_dir)
      intermediate_dir = rebase_path(target_gen_dir)
      args = [
        "--build-method=${build_method}",
        "--cc=${cc}",
        "--cxx=${cxx}",
        "--intermediate-dir=${intermediate_dir}",
        "--jobs=${jobs}",
        "--libdir=lib",
        "--package=${target_name}",
        "--product-dir=${product_dir}",
        "--cflags=${package_cflags}",
        "--ldflags=${package_ldflags}",
        "--sanitizer=${sanitizer_type}",
      ]
      outputs = [
        "${root_out_dir}/instrumented_libraries/${sanitizer_type}/${target_name}.txt",
      ]
      inputs = []
      if (defined(invoker.pre_build)) {
        inputs += [ invoker.pre_build ]
        args += [ "--pre-build=${invoker.pre_build}" ]
      }
      if (defined(invoker.patch)) {
        inputs += [ invoker.patch ]
        args += [ "--patch=${invoker.patch}" ]
      }
      if (defined(invoker.asan_blacklist)) {
        if (is_asan) {
          inputs += [ invoker.asan_blacklist ]
          args += [ "--sanitizer-blacklist=${invoker.asan_blacklist}" ]
        } else {
          assert(invoker.asan_blacklist != "", "")
        }
      }
      if (defined(invoker.msan_blacklist)) {
        if (is_msan) {
          inputs += [ invoker.msan_blacklist ]
          args += [ "--sanitizer-blacklist=${invoker.msan_blacklist}" ]
        } else {
          assert(invoker.msan_blacklist != "", "")
        }
      }

      if (defined(invoker.extra_configure_flags)) {
        args += [ "--extra-configure-flags=${invoker.extra_configure_flags}" ]
      }
    }
  }

  instrumented_library("atk1.0") {
    extra_configure_flags = [
      "--disable-static",

      # See above.
      "--disable-introspection",
    ]
  }

  instrumented_library("brltty") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--without-viavoice",
      "--without-theta",
      "--without-swift",
      "--bindir=/sbin",
      "--with-curses=ncursesw",
      "--disable-stripping",

      # We don't need any of those.
      "--disable-java-bindings",
      "--disable-lisp-bindings",
      "--disable-ocaml-bindings",
      "--disable-python-bindings",
      "--disable-tcl-bindings",
    ]
  }

  instrumented_library("dee") {
    extra_configure_flags = [
      "--disable-static",

      # See above.
      "--disable-introspection",
    ]
    pre_build = "scripts/pre-build/autogen.sh"
  }

  instrumented_library("freetype") {
    pre_build = "scripts/pre-build/freetype.sh"
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("harfbuzz") {
    package_cflags = [ "-Wno-c++11-narrowing" ]
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--with-graphite2=yes",
      "--with-gobject",

      # See above.
      "--disable-introspection",
    ]
  }

  instrumented_library("libappindicator1") {
    extra_configure_flags = [
      "--disable-static",

      # See above.
      "--disable-introspection",
    ]
    jobs = 1
    pre_build = "scripts/pre-build/autogen.sh"
  }

  instrumented_library("libasound2") {
    extra_configure_flags = [ "--disable-static" ]
    pre_build = "scripts/pre-build/libasound2.sh"
  }

  instrumented_library("libcairo2") {
    extra_configure_flags = [
      "--disable-gtk-doc",
      "--disable-static",
    ]
  }

  instrumented_library("libcap2") {
    extra_configure_flags = [ "--disable-static" ]
    build_method = "custom_libcap"
  }

  instrumented_library("libcredentialkit_pkcs11") {
    build_method = "stub"
  }

  instrumented_library("libcups2") {
    patch = "patches/libcups2.diff"
    jobs = 1
    extra_configure_flags = [
      "--disable-static",

      # All from debian/rules.
      "--localedir=/usr/share/cups/locale",
      "--enable-slp",
      "--enable-libpaper",
      "--enable-ssl",
      "--enable-gnutls",
      "--disable-openssl",
      "--enable-threads",
      "--enable-debug",
      "--enable-dbus",
      "--with-dbusdir=/etc/dbus-1",
      "--enable-gssapi",
      "--enable-avahi",
      "--with-pdftops=/usr/bin/gs",
      "--disable-launchd",
      "--with-cups-group=lp",
      "--with-system-groups=lpadmin",
      "--with-printcap=/var/run/cups/printcap",
      "--with-log-file-perm=0640",
      "--with-local_protocols=\"CUPS dnssd\"",
      "--with-remote_protocols=\"CUPS dnssd\"",
      "--enable-libusb",
    ]
    pre_build = "scripts/pre-build/libcups2.sh"
  }

  instrumented_library("libdbus-1-3") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--disable-libaudit",
      "--enable-apparmor",
      "--enable-systemd",
      "--libexecdir=/lib/dbus-1.0",
      "--with-systemdsystemunitdir=/lib/systemd/system",
      "--disable-tests",
      "--exec-prefix=/",

      # From dh_auto_configure.
      "--prefix=/usr",
      "--localstatedir=/var",
    ]
  }

  instrumented_library("libdbus-glib-1-2") {
    extra_configure_flags = [
      # Use system dbus-binding-tool. The just-built one is instrumented but
      # doesn't have the correct RPATH, and will crash.
      "--with-dbus-binding-tool=dbus-binding-tool",
      "--disable-static",
    ]
  }

  instrumented_library("libdbusmenu") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--disable-scrollkeeper",
      "--with-gtk=2",

      # See above.
      "--disable-introspection",
      "--disable-vala",
    ]
    pre_build = "scripts/pre-build/autogen.sh"
  }

  instrumented_library("libdbusmenu-glib4") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--disable-scrollkeeper",
      "--enable-gtk-doc",

      # --enable-introspection introduces a build step that attempts to run
      # a just-built binary and crashes. Vala requires introspection.
      # TODO(eugenis): find a better fix.
      "--disable-introspection",
      "--disable-vala",
    ]
    pre_build = "scripts/pre-build/autogen.sh"
  }

  instrumented_library("libexpat1") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libffi6") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libfontconfig1") {
    extra_configure_flags = [
      "--disable-docs",
      "--sysconfdir=/etc/",
      "--disable-static",

      # From debian/rules.
      "--with-add-fonts=/usr/X11R6/lib/X11/fonts,/usr/local/share/fonts",
    ]
    patch = "patches/libfontconfig.${instrumented_libraries_platform}.diff"
  }

  instrumented_library("libgconf-2-4") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules. (Even though --with-gtk=3.0 doesn't make sense.)
      "--with-gtk=3.0",
      "--disable-orbit",

      # See above.
      "--disable-introspection",
    ]
  }

  instrumented_library("libgcrypt11") {
    package_ldflags = [ "-Wl,-z,muldefs" ]
    extra_configure_flags = [
      # From debian/rules.
      "--enable-noexecstack",
      "--enable-ld-version-script",
      "--disable-static",

      # http://crbug.com/344505
      "--disable-asm",
    ]
  }

  instrumented_library("libgdk-pixbuf2.0-0") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--with-libjasper",
      "--with-x11",

      # Make the build less problematic.
      "--disable-introspection",

      # Do not use loadable modules. Same as with Pango, there's no easy way
      # to make gdk-pixbuf pick instrumented versions over system-installed
      # ones.
      "--disable-modules",
    ]
    pre_build = "scripts/pre-build/libgdk-pixbuf2.0-0.sh"
  }

  instrumented_library("libglib2.0-0") {
    extra_configure_flags = [
      "--disable-gtk-doc",
      "--disable-gtk-doc-html",
      "--disable-gtk-doc-pdf",
      "--disable-static",
    ]
    asan_blacklist = "blacklists/asan/libglib2.0-0.txt"
    msan_blacklist = "blacklists/msan/libglib2.0-0.txt"
    pre_build = "scripts/pre-build/autogen.sh"
  }

  instrumented_library("libgnome-keyring0") {
    extra_configure_flags = [
      "--disable-static",
      "--enable-tests=no",

      # Make the build less problematic.
      "--disable-introspection",
    ]
    package_ldflags = [ "-Wl,--as-needed" ]
  }

  instrumented_library("libgpg-error0") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libgtk2.0-0") {
    package_cflags = [ "-Wno-return-type" ]
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--prefix=/usr",
      "--sysconfdir=/etc",
      "--enable-test-print-backend",
      "--enable-introspection=no",
      "--with-xinput=yes",
    ]
    patch = "patches/libgtk2.0-0.${instrumented_libraries_platform}.diff"
    pre_build = "scripts/pre-build/libgtk2.0-0.sh"
  }

  instrumented_library("libnspr4") {
    extra_configure_flags = [
      "--enable-64bit",
      "--disable-static",

      # TSan reports data races on debug variables.
      "--disable-debug",
    ]
    pre_build = "scripts/pre-build/libnspr4.sh"
  }

  instrumented_library("libp11-kit0") {
    extra_configure_flags = [ "--disable-static" ]

    # Required on Trusty due to autoconf version mismatch.
    pre_build = "scripts/pre-build/autoreconf.sh"
  }

  instrumented_library("libpci3") {
    extra_configure_flags = [ "--disable-static" ]
    build_method = "custom_libpci3"
    jobs = 1
  }

  instrumented_library("libpcre3") {
    extra_configure_flags = [
      "--enable-utf8",
      "--enable-unicode-properties",
      "--disable-static",
    ]
  }

  instrumented_library("libpixman-1-0") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--disable-gtk",
      "--disable-silent-rules",

      # Avoid a clang issue. http://crbug.com/449183
      "--disable-mmx",
    ]
    patch = "patches/libpixman-1-0.diff"
  }

  instrumented_library("libpng12-0") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libsecret") {
    extra_configure_flags = [
      "--disable-static",

      # See above.
      "--disable-introspection",
    ]
    pre_build = "scripts/pre-build/autoreconf.sh"
  }

  instrumented_library("libtasn1-3") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--enable-ld-version-script",
    ]
  }

  instrumented_library("libtasn1-6") {
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--enable-ld-version-script",
    ]
  }

  instrumented_library("libunity9") {
    extra_configure_flags = [ "--disable-static" ]
    pre_build = "scripts/pre-build/autogen.sh"
  }

  instrumented_library("libva1") {
    extra_configure_flags = [ "--disable-static" ]

    # Backport a use-after-free fix:
    # http://cgit.freedesktop.org/libva/diff/va/va.c?h=staging&id=d4988142a3f2256e38c5c5cdcdfc1b4f5f3c1ea9
    patch = "patches/libva1.diff"
    pre_build = "scripts/pre-build/libva1.sh"
  }

  instrumented_library("libx11-6") {
    extra_configure_flags = [
      "--disable-specs",
      "--disable-static",
    ]
    msan_blacklist = "blacklists/msan/libx11-6.txt"

    # Required on Trusty due to autoconf version mismatch.
    pre_build = "scripts/pre-build/autoreconf.sh"
  }

  instrumented_library("libxau6") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxcb1") {
    extra_configure_flags = [
      "--disable-build-docs",
      "--disable-static",
    ]
    pre_build = "scripts/pre-build/autoreconf.sh"
    if (is_precise) {
      patch = "patches/libxcb1.precise.diff"
    }
  }

  instrumented_library("libxcomposite1") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxcursor1") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxdamage1") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxdmcp6") {
    extra_configure_flags = [
      "--disable-docs",
      "--disable-static",
    ]
  }

  instrumented_library("libxext6") {
    extra_configure_flags = [
      "--disable-specs",
      "--disable-static",
    ]
  }

  instrumented_library("libxfixes3") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxi6") {
    extra_configure_flags = [
      "--disable-specs",
      "--disable-docs",
      "--disable-static",
    ]
  }

  instrumented_library("libxinerama1") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxrandr2") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxrender1") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxss1") {
    extra_configure_flags = [ "--disable-static" ]
  }

  instrumented_library("libxtst6") {
    extra_configure_flags = [
      "--disable-specs",
      "--disable-static",
    ]
  }

  instrumented_library("nss") {
    # TODO(eugenis): get rid of this dependency
    deps = [
      ":libnspr4",
    ]
    patch = "patches/nss.diff"
    build_method = "custom_nss"
  }

  instrumented_library("pango1.0") {
    extra_configure_flags = [
      "--disable-static",

      # Avoid https://bugs.gentoo.org/show_bug.cgi?id=425620
      "--enable-introspection=no",

      # Pango is normally used with dynamically loaded modules. However,
      # ensuring pango is able to find instrumented versions of those modules
      # is a huge pain in the neck. Let's link them statically instead, and
      # hope for the best.
      "--with-included-modules=yes",
    ]
  }

  instrumented_library("pulseaudio") {
    # New location of libpulsecommon.
    if (is_precise) {
      patch = "patches/pulseaudio.precise.diff"
      jobs = 1
    }
    if (is_trusty) {
      package_ldflags = [ "-Wl,-R,XORIGIN/pulseaudio/." ]
    }
    extra_configure_flags = [
      "--disable-static",

      # From debian/rules.
      "--enable-x11",
      "--disable-hal-compat",

      # Disable some ARM-related code that fails compilation. No idea why
      # this even impacts x86-64 builds.
      "--disable-neon-opt",
    ]
    pre_build = "scripts/pre-build/pulseaudio.sh"
  }

  instrumented_library("udev") {
    extra_configure_flags = [
      "--disable-static",

      # Without this flag there's a linking step that doesn't honor LDFLAGS
      # and fails.
      # TODO(eugenis): find a better fix.
      "--disable-gudev",
    ]
    pre_build = "scripts/pre-build/udev.sh"
  }

  instrumented_library("zlib1g") {
    # --disable-static is not supported
    patch = "patches/zlib1g.diff"
  }
}
