import intf_libs  = libboost-assert%lib{boost_assert}
import intf_libs += libboost-config%lib{boost_config}
import intf_libs += libboost-predef%lib{boost_predef}
import intf_libs += libboost-type-traits%lib{boost_type_traits}
import intf_libs += libboost-winapi%lib{boost_winapi}
import impl_libs  = libboost-align%lib{boost_align}
import impl_libs += libboost-preprocessor%lib{boost_preprocessor}

intf_libs = $cxx.deduplicate_export_libs($intf_libs)

# Public headers.
#
pub = [dir_path] ../include/boost/

include $pub

pub_hdrs = $($pub/ pub_hdrs)

# BEGIN MANUAL CUSTOMIZATION
#

lib{boost_atomic}: $pub/{$pub_hdrs}

tclass  = $cxx.target.class
tsys    = $cxx.target.system
windows = ($tclass == 'windows')

# Enable SSE4.1 if compiling for i686 or x86_64.
#
# The upstream build detects SSE2 and SSE4.1 support in the compiler (not the
# CPU, though this is implied) by trying to compile a SSE2 program and a
# SSE4.1 program. Depending on which of the compilations succeeded it defines
# the private macros BOOST_ATOMIC_USE_SSE2 and/or BOOST_ATOMIC_USE_SSE41 and
# adds `find_address_sse2.cpp` and/or `find_address_sse41.cpp` to the
# library's sources. These source files provide SSE-based implementations of
# the private find_address() function (note: not part of Boost.Atomic's public
# interface). If both versions are supported Boost.Atomic will choose SSE4.1.
# If SSE is not enabled, Boost.Atomic will fall back to a generic
# implementation of find_address() (a sequential search for a specific `void*`
# in an array of `void*`s).

# Set `sse` to true if the CPU and compiler supports SSE4.1.
#
# Given that Intel's Penryn processor family, released in 2006, was the first
# to support SSE4.1, assume SSE4.1 is available on all i686 and x86_64 CPUs
# and all supported compilers. Disable SSE on all other targets.
#
sse = false

# Determine the SSE4.1 compilation options appropriate for the compiler.
#
if ($cxx.target.cpu == 'x86_64' || $cxx.target.cpu == 'i686')
{
  sse = true

  # Note that ICC on POSIX is in the `gcc` class while on Windows it is in the
  # `msvc` class.
  #
  switch $cxx.class
  {
    case 'gcc'
      sse_opts = ($cxx.id == 'icc'                        \
                  ? -xSSE4.1                              \
                  : -msse -msse2 -msse3 -mssse3 -msse4.1)

    case 'msvc'
      # Add no compiler options in case of MSVC because, on x86_64, neither
      # SSE2 nor SSE4.1 can be turned on explicitly. There is an SSE2 option
      # but it's not applicable in our case, and the only other options (see
      # https://docs.microsoft.com/en-us/cpp/build/reference/arch-x64) are
      # various versions of AVX which appears to have some overlap with
      # SSE4.1, but the upstream build does not turn any of the AVX options
      # on.
      #
      sse_opts = ($cxx.id == 'icc' ? /QxSSE4.1 : )
  }
}

# Private headers and sources as well as dependencies.
#

# Initially exclude the source files dependent on SSE support in the CPU (but
# they may be added back later; see below).
#
lib{boost_atomic}: {hxx ixx txx cxx}{** -find_address_sse*} \
                   $impl_libs $intf_libs

# If SSE is enabled, add the SSE4.1 find_address() implementation to the
# sources.
#
lib{boost_atomic}: cxx{find_address_sse41}: include = $sse

# On Windows, Boost.Atomic's versions of
# std::atomic<>::{wait,notify_one,notify_all}() (added in C++20) are
# implemented with the WaitOnAddress(), WakeByAddressSingle(), and
# WakeByAddressAll() system calls, respectively. These functions are defined
# in `api-ms-win-core-synch-l1-2-0.dll` which Boost.Atomic expects to be
# available on all versions of Windows.
#
# Windows 8 (2012) added an import library for
# `api-ms-win-core-synch-l1-2-0.dll` called `synchronization.lib` which comes
# with the Windows SDK (according to
# https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress).
#
# On Windows, Boost.Atomic links `synchronization` (the import library for
# api-ms-win-core-synch-l1-2-0.dll, available since Windows 8) for the
# WaitOnAddress() family of functions. This linking is handled in the switch
# below.
#
# Note: as of Boost 1.91.0 this functionality is implemented entirely in
# headers (detail/wait_ops_windows.hpp) and the separate wait_on_address.cpp
# source file that earlier versions compiled has been removed, so there is no
# longer a Windows-specific source to conditionally add here.
#
# As a sidenote, on Linux, NetBSD, and OpenBSD, a futex-based backend is
# used. On FreeBSD and DragonFlyBSD the umtx system calls are used. On older
# versions of these OSs a generic (lock-based) backend is used as fallback.

#
# END MANUAL CUSTOMIZATION

# Build options.
#
out_pfx_inc = [dir_path] $out_root/include/
src_pfx_inc = [dir_path] $src_root/include/
out_pfx_src = [dir_path] $out_root/src/
src_pfx_src = [dir_path] $src_root/src/

cxx.poptions =+ "-I$out_pfx_src" "-I$src_pfx_src" \
                "-I$out_pfx_inc" "-I$src_pfx_inc"

{hbmi  obj }{*}: cxx.poptions += -DBOOST_ATOMIC_SOURCE
{hbmia obja}{*}: cxx.poptions += -DBOOST_ATOMIC_STATIC_LINK
{hbmis objs}{*}: cxx.poptions += -DBOOST_ATOMIC_DYN_LINK

# BEGIN MANUAL CUSTOMIZATION
#

if $sse
{
  cxx.poptions += -DBOOST_ATOMIC_USE_SSE41

  # Add the SSE compiler options to the `find_address_sse41` object files.
  #
  {hbmi obj}{find_address_sse41}: cxx.coptions += $sse_opts
}

# Link to synchronization.lib on Windows.
#
# This works only for Windows >= 8; on older versions no library should be
# added (see above).
#
switch $tclass, $tsys
{
  case 'windows', 'mingw32'
  {
    # MinGW-w64 defaults _WIN32_WINNT to _WIN32_WINNT_WS03, which disables the
    # WaitOnAddress API that Boost.Atomic requires (now header-only; see
    # above) and raise it to Windows 10.
    #
    # https://github.com/boostorg/atomic/issues/73
    #
    cxx.poptions += -D_WIN32_WINNT=0x0A00
    cxx.libs += -lsynchronization
    lib{boost_atomic}: cxx.export.libs = -lsynchronization
  }
  case 'windows'
  {
    cxx.libs += synchronization.lib
    lib{boost_atomic}: cxx.export.libs = synchronization.lib
  }
}

# On POSIX, Boost.Atomic's lock_pool uses pthread condition variables
# (pthread_condattr_init/setclock/destroy), so link pthread. With glibc these
# are part of libc, but on FreeBSD (and the other BSDs) they live in libpthread
# and the shared library would otherwise have undefined references under
# --no-allow-shlib-undefined. Export it as well so static consumers link it
# too.
#
if ($tclass != 'windows')
  cxx.libs += -pthread

# Export options.
#
lib{boost_atomic}:
{
  cxx.export.poptions = "-I$out_pfx_inc" "-I$src_pfx_inc" \
                         -DBOOST_ATOMIC_NO_LIB
  cxx.export.libs += $intf_libs
}

if ($tclass != 'windows')
  lib{boost_atomic}: cxx.export.libs += -pthread

# MinGW consumers that use Boost.Atomic's header-only wait/notify also need
# _WIN32_WINNT raised (see the switch above). Append after the export.poptions
# assignment above so it is not overwritten.
#
if ($tclass == 'windows' && $tsys == 'mingw32')
  lib{boost_atomic}: cxx.export.poptions += -D_WIN32_WINNT=0x0A00

#
# END MANUAL CUSTOMIZATION

liba{boost_atomic}: cxx.export.poptions += -DBOOST_ATOMIC_STATIC_LINK
libs{boost_atomic}: cxx.export.poptions += -DBOOST_ATOMIC_DYN_LINK

# For pre-releases use the complete version to make sure they cannot be used
# in place of another pre-release or the final version. See the version module
# for details on the version.* variable values.
#
if $version.pre_release
  lib{boost_atomic}: bin.lib.version = "-$version.project_id"
else
  lib{boost_atomic}: bin.lib.version = "-$version.major.$version.minor"

# Don't install private headers.
#
{hxx ixx txx}{*}: install = false
