using autoconf

intf_libs = # Interfaces dependencies.
impl_libs = # Implementation dependencies.

import intf_libs = libvulkan-headers%lib{vulkan-headers}
import impl_libs = libcjson%lib{cjson}

import! asm_offset = vulkan-loader-asm-offset%exe{asm_offset}

define masm: file
masm{*}: extension = asm

windows  = ($c.target.class == 'windows')
linux    = ($c.target.class == 'linux')
bsd      = ($c.target.class == 'bsd')
macos    = ($c.target.class == 'macos')
mingw    = ($c.target.system == 'mingw32')
msvc_sys = ($c.target.system == 'win32-msvc')
x86      = ($c.target.cpu == 'x86_64' || $regex.match($c.target.cpu, 'i[3-6]86'))
x86_64   = ($c.target.cpu == 'x86_64')
arm      = ($c.target.cpu == 'aarch64' || $regex.match($c.target.cpu, 'arm.*'))
aarch64  = ($c.target.cpu == 'aarch64')

# Unknown-extension trampolines: MASM/MARMASM on Windows, GAS on Unix.
#
gcc_or_clang = ($c.class == 'gcc' || $c.class == 'clang')
use_gas      = (!$windows && $gcc_or_clang && ($x86 || $arm))
use_masm     = ($windows && $x86)
use_marmasm  = ($windows && $arm)

# Locate MASM/MARMASM next to $c.path, else PATH. Soft-disable if missing.
#
masm_asm = [path]
masm_copts = [strings]
tool = [string]

switch $c.target.system, $c.target.cpu: path.match
{
  case 'win32-msvc', 'aarch64'
  case 'mingw32', 'aarch64'
  {
    tool = 'armasm64.exe'
    masm_copts = [strings] -nologo -c
  }
  case 'win32-msvc', 'arm*'
  case 'mingw32', 'arm*'
  {
    tool = 'armasm.exe'
    masm_copts = [strings] -nologo -c
  }
  case 'mingw32', 'x86_64'
  {
    tool = 'jwasm'
    masm_copts = [strings] -win64 -c
  }
  case 'mingw32'
  {
    tool = 'jwasm'
    masm_copts = [strings] -3 -coff -c
  }
  case 'win32-msvc', 'i[3-6]86'
  {
    tool = 'ml.exe'
    masm_copts = [strings] -nologo -c -safeseh
  }
  case 'win32-msvc'
  {
    tool = 'ml64.exe'
    masm_copts = [strings] -nologo -c
  }
}

if ($use_masm || $use_marmasm)
{
  c_dir = [dir_path] $directory($effect($c.path))
  ml_path = [path] $c_dir/$tool
  if (!$file_exists($ml_path))
    ml_path = [path] $process.search($tool)

  if ($mingw && ($null($ml_path) || !$file_exists($ml_path)))
  {
    tool = 'uasm'
    ml_path = [path] $c_dir/$tool
    if (!$file_exists($ml_path))
      ml_path = [path] $process.search($tool)
  }

  if ($null($ml_path) || !$file_exists($ml_path))
  {
    if ($build.meta_operation == 'configure')
      info "assembler $tool not found, disabling unknown-function MASM"

    use_masm = false
    use_marmasm = false
    masm_asm = [path]
  }
  else
    masm_asm = [path] $ml_path
}

use_unknown_asm = ($use_gas || $use_masm || $use_marmasm)

# gen_defines.asm comes from running host asm_offset (sizeof/offsetof). That is
# only valid when host and target share CPU and OS class. Cross builds need a
# target-compiler intermediate parse (upstream parse_asm_values.py), which we
# do not implement. Fail rather than link trampolines with wrong constants.
#
if $use_unknown_asm
{
  if ($c.target.cpu != $build.host.cpu)
    fail 'unknown-function ASM cannot cross-compile (host asm_offset would emit host offsets)'

  if ($c.target.class != $build.host.class)
    fail 'unknown-function ASM cannot cross-compile (host asm_offset would emit host offsets)'
}

asm_dialect = ($use_marmasm ? MARMASM : ($use_masm ? MASM : GAS))

# HAVE_CET_H (GAS x86) and HAVE_ALLOCA_H via autoconf + c.predefs.
#
h{loader-config}: in{loader-config}

[rule_hint=c.predefs] \
buildfile{loader-config}: h{loader-config}
{
  c.predefs.poptions = false
  c.predefs.macros = HAVE_CET_H HAVE_ALLOCA_H
}

./: lib{vulkan}
./: buildfile{loader-config}

have_cet_h = [bool] false
have_alloca_h = [bool] false

if ($build.meta_operation == 'perform')
{
  update buildfile{loader-config}
  source $path(buildfile{loader-config})
  have_cet_h = ($HAVE_CET_H == 1 ? true : false)
  have_alloca_h = ($HAVE_ALLOCA_H == 1 ? true : false)
}

# WSI platforms follow the headers on the compiler search path.
#
have_xcb_xcb_h = [bool] ($c.find_system_header("xcb/xcb.h") != [null])
have_x11_xlib_h = [bool] ($c.find_system_header("X11/Xlib.h") != [null])
have_x11_extensions_xrandr_h = [bool] \
  ($c.find_system_header("X11/extensions/Xrandr.h") != [null])
have_wayland_client_h = [bool] \
  ($c.find_system_header("wayland-client.h") != [null])

# Build options.
#
c.poptions =+ "-I$out_base" "-I$src_base" "-I$src_base/generated"

# VK_ENABLE_BETA_EXTENSIONS is required globally: the generated
# vk_object_types.h uses beta-guarded enumerants unconditionally
# (matching the upstream build).
#
c.poptions += -DVK_ENABLE_BETA_EXTENSIONS

if $use_unknown_asm
  c.poptions += -DUNKNOWN_FUNCTIONS_SUPPORTED

if $have_alloca_h
  c.poptions += -DHAVE_ALLOCA_H

if $use_gas
{
  if $macos
    c.poptions += -DMODIFY_UNKNOWN_FUNCTION_DECLS

  if $have_cet_h
    c.poptions += -DHAVE_CET_H
}

if $windows
{
  c.poptions += -DWIN32_LEAN_AND_MEAN -DVK_USE_PLATFORM_WIN32_KHR
  if $msvc_sys
    c.libs += cfgmgr32.lib advapi32.lib
  else
    c.libs += -lcfgmgr32 -ladvapi32
}
else
{
  # alloca() and secure_getenv() require _GNU_SOURCE on glibc.
  #
  c.poptions += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64

  c.poptions += -DFALLBACK_CONFIG_DIRS=\"/etc/xdg\" \
                -DFALLBACK_DATA_DIRS=\"/usr/local/share:/usr/share\" \
                -DSYSCONFDIR=\"/etc\"

  if ($linux || $bsd)
    c.poptions += -DLOADER_ENABLE_LINUX_SORT \
                  -DHAVE_SECURE_GETENV

  if $have_xcb_xcb_h
    c.poptions += -DVK_USE_PLATFORM_XCB_KHR

  if $have_x11_xlib_h
    c.poptions += -DVK_USE_PLATFORM_XLIB_KHR

  if $have_x11_extensions_xrandr_h
    c.poptions += -DVK_USE_PLATFORM_XLIB_XRANDR_EXT

  if $have_wayland_client_h
    c.poptions += -DVK_USE_PLATFORM_WAYLAND_KHR

  if $bsd
    c.poptions += -D__BSD_VISIBLE=1

  if $macos
  {
    # Match upstream: Metal plus the Darwin MoltenVK surface extension.
    #
    c.poptions += -DVK_USE_PLATFORM_METAL_EXT \
                  -DVK_USE_PLATFORM_MACOS_MVK
  }

  # On lib{} only: directory-scope c.libs is applied twice with libul when
  # linking libs{} (duplicate -ldl/-lm).
  #
  lib{vulkan}: c.libs += -ldl -lm
  c.loptions += -pthread
}

if (!$windows && $gcc_or_clang)
  c.coptions += -fvisibility=hidden

if $mingw
  c.loptions += -Wl,--export-all-symbols

if $macos
  c.libs += -framework CoreFoundation

# asm_offset writes "gen_defines.asm" in its process cwd.
#
h{gen_defines.asm}: $asm_offset
{{
  diag gen $>
  env -c $directory($path($>)) -- $path($<[0]) $asm_dialect
}}

# Excluded sources:
#   asm_offset.c                     - in vulkan-loader-asm-offset package
#   dev_ext_trampoline.c / phys_dev_ext.c - optional ASM, include= below
#   dlopen_fuchsia.c                 - Fuchsia-only
#   loader_windows.c / dirent_on_windows.c - Windows (conditional)
#   loader_linux.c                   - Linux/BSD (conditional)
#   generated/vk_loader_extensions.c - #included by loader.c, not a TU
#
lib{vulkan}: libul{vulkan}: h{** -loader-config -gen_defines.asm} \
                             file{generated/vk_loader_extensions.c} \
                             c{** -asm_offset -dev_ext_trampoline -phys_dev_ext \
                                  -dlopen_fuchsia \
                                  -loader_windows -dirent_on_windows \
                                  -loader_linux \
                                  -generated/vk_loader_extensions} \
                             $impl_libs $intf_libs

libul{vulkan}: c{loader_windows} c{dirent_on_windows}: \
  include = $windows

libul{vulkan}: c{loader_linux}: \
  include = ($linux || $bsd)

libul{vulkan}: c{dev_ext_trampoline} c{phys_dev_ext}: include = $use_unknown_asm

libul{vulkan}: S{unknown_ext_chain_gas_x86}: \
  include = ($use_gas && $x86)
libul{vulkan}: S{unknown_ext_chain_gas_aarch}: \
  include = ($use_gas && $arm)

libul{vulkan}: h{gen_defines.asm}: include = $use_unknown_asm

libul{vulkan}: masm{unknown_ext_chain_masm}: include = $use_masm
libul{vulkan}: masm{unknown_ext_chain_marmasm}: include = $use_marmasm

libus{vulkan}: objs{unknown_ext_chain_masm.masm.so.obj}: include = $use_masm
libua{vulkan}: obja{unknown_ext_chain_masm.masm.a.obj}:  include = $use_masm

libus{vulkan}: objs{unknown_ext_chain_marmasm.masm.so.obj}: include = $use_marmasm
libua{vulkan}: obja{unknown_ext_chain_marmasm.masm.a.obj}:  include = $use_marmasm

objs{unknown_ext_chain_masm.masm.so.obj}: \
  masm{unknown_ext_chain_masm} h{gen_defines.asm}
obja{unknown_ext_chain_masm.masm.a.obj}: \
  masm{unknown_ext_chain_masm} h{gen_defines.asm}

objs{unknown_ext_chain_marmasm.masm.so.obj}: \
  masm{unknown_ext_chain_marmasm} h{gen_defines.asm}
obja{unknown_ext_chain_marmasm.masm.a.obj}: \
  masm{unknown_ext_chain_marmasm} h{gen_defines.asm}

objs{~'/(.*).masm.so/'}: masm{~'/\1/'} $masm_asm
{{
  diag asm ($<[0]) -> ($>[0])
  $masm_asm $masm_copts "-I$out_base" -Fo $path($>[0]) $path($<[0]) >!
}}

obja{~'/(.*).masm.a/'}: masm{~'/\1/'} $masm_asm
{{
  diag asm ($<[0]) -> ($>[0])
  $masm_asm $masm_copts "-I$out_base" -Fo $path($>[0]) $path($<[0]) >!
}}

libs{vulkan}: def{vulkan}: include = $msvc_sys
def{vulkan}: libul{vulkan}

# Export options.
#
# Public headers come from libvulkan-headers (interface dependency).
# Loader-internal headers are not installed.
#
lib{vulkan}:
{
  c.export.libs = $intf_libs

  # Build-time metadata for consumers (also written to pkg-config).
  #
  export.metadata = 1 libvulkan_loader

  libvulkan_loader.unknown_functions = [bool] $use_unknown_asm
}

# 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.
#
# A final release uses the same shared library names as upstream CMake:
# vulkan-1.dll, libvulkan.so.$version with SONAME libvulkan.so.1, and
# libvulkan.1.dylib. linux@ needs MAJOR.MINOR[.EXTRA], so the project
# version is passed through and the SONAME stays 1. macos@ and bsd@
# are not implemented. Apple gets the compatibility name (the
# LC_ID_DYLIB and dlopen name). BSD ($c.target.class == 'bsd') sets
# the shared extension to so.$major so the file is libvulkan.so.1
# (the SONAME and dlopen name).
#
if $version.pre_release
  lib{vulkan}: bin.lib.version = "-$version.project_id"
elif $macos
  lib{vulkan}: bin.lib.version = ".$version.major"
elif $bsd
  libs{vulkan}: extension = "so.$version.major"
else
  lib{vulkan}: bin.lib.version = "-$version.major" \
    linux@"$version.major.$version.minor.$version.patch"

# Upstream MinGW clears PREFIX so the DLL is vulkan-1.dll, not
# libvulkan-1.dll.
#
if $mingw
  libs{vulkan}: bin.lib.prefix = ""

# Private headers are not installed (public API is libvulkan-headers).
#
h{*}: install = false
