# -*- mode: python -*-

Import("env")
Import("get_option")
Import("has_option")
Import("use_libunwind")
Import("debugBuild")
Import("selected_experimental_optimizations")

env = env.Clone(
    # Building with hidden visibility interferes with intercepting the
    # libc allocation functions.
    DISALLOW_VISHIDDEN=True,
    NINJA_GENSOURCE_INDEPENDENT=True,
)

# Enabling the function sanitizer (part of ubsan) references symbols defined in a separate file when compiled with
# dynamic linking in Clang. The C program definitions setup in SCons do not link this separate file.
# Disable function sanitizer to avoid triggering symbol resolution errors when libraries defined in this
# environment are used by C programs (in this case, wiredtiger).
if get_option("link-model") == "dynamic" and 'undefined' in (get_option('sanitize')
                                                             or '').split(','):
    if env.AddToCCFLAGSIfSupported("-fno-sanitize=function"):
        env.AppendUnique(LINKFLAGS=["-fno-sanitize=function"])

# If we don't have a frame pointer, we need to tell tcmalloc so that
# it doesn't try to select a frame pointer based unwinder like
# generic_fp. For debugBuilds, we also don't want the generic_fp or
# libunwind based unwinders because they are currently too slow. We
# will still fall back to libunwind if it is present and no better
# option is available. Note that defining
# TCMALLOC_DONT_PREFER_LIBUNWIND has real effects even if libunwind
# isn't in play. Specifically, it enables the architecture specific
# implementations for x86 and ppc.
#
# Please see the following gperftools issues for ongoing discussion:
#
# https://github.com/gperftools/gperftools/issues/1276
# https://github.com/gperftools/gperftools/issues/1277
# https://github.com/gperftools/gperftools/issues/1278
#

fp = 'nofp' not in selected_experimental_optimizations
if not fp or debugBuild:
    env.Append(
        CPPDEFINES=[
            'NO_FRAME_POINTER',
            'TCMALLOC_DONT_PREFER_LIBUNWIND' if debugBuild else None,
        ], )

if use_libunwind:
    env.Append(CPPDEFINES=[
        ("HAVE_LIBUNWIND_H", "1"),
        'HAVE_UCONTEXT_H',
    ], )
    env.InjectThirdParty(libraries=['unwind'])

files = [
    'src/base/dynamic_annotations.c',
    'src/base/elf_mem_image.cc',
    'src/base/logging.cc',
    'src/base/spinlock.cc',
    'src/base/spinlock_internal.cc',
    'src/base/sysinfo.cc',
    'src/base/vdso_support.cc',
    'src/central_freelist.cc',
    'src/common.cc',
    'src/internal_logging.cc',
    'src/malloc_extension.cc',
    'src/malloc_hook.cc',
    'src/memfs_malloc.cc',
    'src/page_heap.cc',
    'src/sampler.cc',
    'src/span.cc',
    'src/stack_trace_table.cc',
    'src/stacktrace.cc',
    'src/static_vars.cc',
    'src/symbolize.cc',
    'src/thread_cache.cc',
]

if env.TargetOSIs('windows'):
    files += [
        'src/tcmalloc.cc',
        'src/windows/port.cc',
        'src/windows/system-alloc.cc',
        'src/fake_stacktrace_scope.cc',
    ]

    # warning C4141: 'inline': used more than once
    # warning C4305: 'argument': truncation from 'ssize_t' to 'double'
    # warning C4003: not enough arguments for function-like macro invocation
    env.Append(CXXFLAGS=["/wd4141", "/wd4305", '/wd4003'])

else:
    files += [
        'src/emergency_malloc_for_stacktrace.cc',
        'src/maybe_threads.cc',
        'src/system-alloc.cc',
    ]

    if not debugBuild:
        files += ['src/tcmalloc.cc']
    else:
        files += ['src/debugallocation.cc']

    # gperftools has some sloppy write calls that emit warnings
    env.Append(CXXFLAGS=["-Wno-unused-result"])

env.Append(CPPDEFINES=["NO_HEAP_CHECK"], )

# The build system doesn't define NDEBUG globally for historical reasons, however, TCMalloc
# expects that NDEBUG is used to select between preferring the mmap or the sbrk allocator. For
# non-debug builds, we want to prefer the sbrk allocator since this is TCMallocs preferred
# production deployment configuration. See the use of NDEBUG and kDebugMode in
# src/system-alloc.cc for more details.
if not debugBuild:
    env.Append(CPPDEFINES=["NDEBUG"])

# For debug builds we want to capture stacks during (de)allocations,
# but we don't want to pay that cost for release builds. For non-debug
# builds we use NO_TCMALLOC_SAMPLES to disable the stack trace
# collection. For debug builds we enable stack capture, but only on
# intel targets, since tcmalloc's unwinder is very slow on other
# platforms (see SERVER-28502).
if (not debugBuild) or (not env['TARGET_ARCH'] in ['x86_64', 'i386']):
    env.Append(CPPDEFINES=["NO_TCMALLOC_SAMPLES"])

gperftools_root = env.Dir("#/src/third_party/gperftools")
gperftools_platform = gperftools_root.Dir("platform/${TARGET_OS}_${TARGET_ARCH}")

env.Append(CPPPATH=[
    gperftools_platform.Dir("internal/src"),
    gperftools_root.Dir("dist/src"),
])

# propagates to consumers that Inject (depend on) gperftools.
env.RegisterConsumerModifications(CPPPATH=[gperftools_platform.Dir("include")])


def removeIfPresent(lst, item):
    try:
        lst.remove(item)
    except ValueError:
        pass


env['CCFLAGS_WERROR'] = []
env['CXXFLAGS_WERROR'] = []
for to_remove in ["-Wsign-compare", "-Wall"]:
    removeIfPresent(env['CCFLAGS'], to_remove)

if not env.TargetOSIs('windows'):
    env.Append(CXXFLAGS=['-Wno-unused-result'])
    if env.ToolchainIs('GCC'):
        env.Append(CXXFLAGS=['-Wno-attribute-alias'])

# GCC on PowerPC under C++11 mode does not define __linux which gperftools depends on
if env['TARGET_ARCH'] == 'ppc64le':
    env.Append(CPPDEFINES=["__linux"])

env.BazelLibrary(
    target='tcmalloc_minimal',
    source=[env.Dir('dist').File(f) for f in files],
    LIBDEPS_PRIVATE=[
        '$BUILD_DIR/third_party/shim_unwind' if use_libunwind else [],
    ],
)
