intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.

pub = [dir_path] ../include/MoltenVK/

include $pub

pub_hdrs = $($pub/ pub_hdrs)

# Mixed-case lib{MoltenVK}: upstream ICD json and -lMoltenVK expect this name.
#
# Declared here, before the imports below, so the lib{}/liba{}/libs{} group
# exists as early as possible in this buildfile. libspirv-cross%liba{} pulls
# in a full nested project bootstrap, and a concurrent importer of
# libs{MoltenVK} (e.g. libmoltenvk-tests) that reaches this scope while that
# bootstrap is still in progress would otherwise see the file as "already
# included" but the target not yet declared (unknown imported target).
# See build2 issue #563.
#
lib{MoltenVK}: $pub/{$pub_hdrs}

# Dylib path for direct driver loading (VK_LUNARG_direct_driver_loading):
# a consumer imports libs{MoltenVK} as an adhoc (not linked) prerequisite
# to build the dylib without exposing its vk* symbols, dlopens this path
# at runtime, and hands the loader vk_icdGetInstanceProcAddr via
# VkDirectDriverLoadingInfoLUNARG. No ICD json, no environment variable.
#
libs{MoltenVK}:
{
  export.metadata = 1 libmoltenvk

  libmoltenvk.dylib_path = [path] $out_root/src/libMoltenVK.dylib

  # Relative to $install.bin (a consumer executable's own install
  # location), not absolute: always-relative so this stays correct
  # under config.install.relocatable=true (see $install.resolve). An
  # installed consumer completes this relative path at runtime by
  # resolving the executable's path and using its directory as the
  # base, then dlopen().
  #
  libmoltenvk.dylib_path.for_install = ($install.root != [null] \
    ? [path] $install.resolve($install.lib, $install.bin)/libMoltenVK.dylib \
    : [path])

  # ICD json path, for libmoltenvk-tests/icd (see ../icd/buildfile, where
  # the actual json{} targets and their install location are declared).
  # Piggybacked here, on libs{MoltenVK}'s own export.metadata, rather than
  # on either json{} target directly: importing a target only loads the
  # buildfile where it is declared, not every buildfile that later adds
  # variables to it, so this has to live wherever the consumer's import
  # actually resolves to, not in ../icd/buildfile.
  #
  libmoltenvk.icd_json = [path] $out_root/icd/MoltenVK_icd.json
  libmoltenvk.icd_json.for_install = ($install.root != [null] \
    ? [path] $install.resolve([dir_path] etc/vulkan/icd.d/, [dir_path])/MoltenVK_icd.json \
    : [path])

  # For libmoltenvk-tests/bundle/buildfile.
  #
  libmoltenvk.icd_json_bundle = [path] $out_root/icd/MoltenVK_icd-bundle.json
  libmoltenvk.icd_json_bundle.for_install = ($install.root != [null] \
    ? [path] $install.resolve($install.data, [dir_path])/MoltenVK_icd-bundle.json \
    : [path])

  # Absolute regardless of config.install.relocatable, unlike
  # dylib_path.for_install above: for libmoltenvk-tests/direct-driver-loading's
  # own test binary, which (unlike a real direct driver loading consumer)
  # is never itself installed, so dylib_path.for_install's relative path
  # can never be completed against it. Same purpose as icd_json above:
  # lets that one test find and dlopen the dylib regardless of
  # installation topology.
  #
  libmoltenvk.test_dylib_path = [path] $out_root/src/libMoltenVK.dylib
  libmoltenvk.test_dylib_path.for_install = ($install.root != [null] \
    ? [path] $install.resolve($install.lib, [dir_path])/libMoltenVK.dylib \
    : [path])
}

# Public API headers include <vulkan/vulkan.h>.
#
import intf_libs += libvulkan-headers%lib{vulkan-headers}

# SPIRV-Cross is used only from private sources (implementation dependency).
# Component/namespace selection is in the package manifest. Import liba{}
# explicitly: SPIRV-Cross's C++ components are static-only (upstream's C++
# API isn't ABI-stable, only its separate C API is).
#
import impl_libs += libspirv-cross%liba{spirv-cross-core}    \
                    libspirv-cross%liba{spirv-cross-glsl}    \
                    libspirv-cross%liba{spirv-cross-msl}     \
                    libspirv-cross%liba{spirv-cross-reflect}

# SPIRV-Tools is used only from SPIRVSupport.cpp (logSPIRV disassembly).
# Always linked, matching upstream MVK_EXCLUDE_SPIRV_TOOLS default of off (the
# exclude define is a presence test, so it is omitted rather than set to 0).
#
# cereal is used only from MVKPipeline.mm (pipeline cache serialization).
# Always linked, matching upstream MVK_EXCLUDE_CEREAL default of off.
#
import impl_libs += libspirv-tools%lib{SPIRV-Tools} \
                    libcereal%lib{cereal}

# Private sources. MoltenVK/API/ is excluded: same files as public headers.
#
# MoltenVK/Vulkan/vulkan.mm is a manually patched copy (see .orig/.patch next
# to it, and KhronosGroup/MoltenVK#2663): vk_icdNegotiateLoaderICDInterfaceVersion
# caps the negotiated interface at version 5, below the version 7 the Vulkan
# loader requires to accept a VK_LUNARG_direct_driver_loading driver (see
# libmoltenvk-tests/direct-driver-loading/). Upstream confirmed bumping this
# one value resolves it.
#
# MoltenVK/GPUObjects/MVKDevice.mm is likewise a manually patched copy (see
# .orig/.patch next to it): supplies the MTLGPUFamilyApple9 enumerator when
# an Xcode 15.0 beta SDK doesn't declare it yet, so the build doesn't fail
# on beta toolchains older than upstream's own stated Xcode 15.0.1 minimum.
#
lib{MoltenVK}: Common/{h cxx m mm}{*}                  \
               MoltenVKShaderConverter/{h cxx m mm}{*} \
               MoltenVK/{h hxx cxx}{** -API/**}        \
               MoltenVK/m{** -API/**}                  \
               MoltenVK/mm{** -API/**}                 \
               $impl_libs $intf_libs

# .def X-macro fragments (#include "MVKExtensions.def" etc.): adhoc since
# they're included, not linked (not the link rule's Windows
# module-definition usage).
#
lib{MoltenVK}: MoltenVK/Layers/def{MVKExtensions}: include = adhoc
lib{MoltenVK}: MoltenVK/GPUObjects/def{MVKDeviceFeatureStructs}: include = adhoc
lib{MoltenVK}: MoltenVK/Commands/def{MVKCommandTypePools}: include = adhoc
lib{MoltenVK}: MoltenVK/Utility/def{MVKConfigMembers}: include = adhoc
lib{MoltenVK}: MoltenVK/Utility/def{MVKDXTnCodec}: include = adhoc

# 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/

# Private -I paths: upstream PRIVATE include dirs plus Common/ and $out_base
# (generated mvkGitRevDerived.h). Internal sources include the public API
# headers unprefixed (#include "mvk_vulkan.h"), resolved via the already-
# tracked include/MoltenVK/ symlinks rather than MoltenVK/API/ directly
# (which lib{MoltenVK}'s own source glob below excludes).
#
moltenvk_ipaths = "-I$src_base/Common"              \
                  "-I$out_pfx_inc/MoltenVK"         \
                  "-I$src_pfx_inc/MoltenVK"         \
                  "-I$src_base/MoltenVK/Commands"   \
                  "-I$src_base/MoltenVK/GPUObjects" \
                  "-I$src_base/MoltenVK/Layers"     \
                  "-I$src_base/MoltenVK/OS"         \
                  "-I$src_base/MoltenVK/Utility"    \
                  "-I$src_base/MoltenVK/Vulkan"     \
                  "-I$out_base"

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

log_level_num = ($config.libmoltenvk.log_level == 'off'   ? 0 : \
                  $config.libmoltenvk.log_level == 'error' ? 1 : \
                  $config.libmoltenvk.log_level == 'warn'  ? 2 : \
                  $config.libmoltenvk.log_level == 'info'  ? 3 : 4)

# MVK_FRAMEWORK_VERSION is diagnostic-only (matches upstream package version).
#
cc.poptions += -DMVK_FRAMEWORK_VERSION=1.4.2 \
               "-DMVK_CONFIG_LOG_LEVEL=$log_level_num" \
               "-DMVK_USE_METAL_PRIVATE_API=($config.libmoltenvk.use_metal_private_api ? 1 : 0)"

# After config.cxx.coptions so -Wextra does not re-enable them.
# unknown-warning-option: older clang lacks -Wno-vla-cxx-extension.
# deprecated-declarations, vla-cxx-extension: upstream CMake.
# mismatched-tags, missing-braces: harmless upstream patterns.
# unguarded-availability-new: MVK_XCODE_26 vs older deployment target.
# unused-parameter, unused-variable: vulkan.mm stubs.
# ignored-qualifiers: const on by-value returns.
#
if ($cxx.id.type == 'clang')
  cxx.coptions += -Wno-unknown-warning-option \
                  -Wno-deprecated-declarations -Wno-vla-cxx-extension \
                  -Wno-mismatched-tags -Wno-missing-braces \
                  -Wno-unguarded-availability-new \
                  -Wno-unused-parameter -Wno-unused-variable \
                  -Wno-ignored-qualifiers

# Static revision string for diagnostics (no live git rev-parse).
#
h{mvkGitRevDerived}: in{mvkGitRevDerived.h.in}
h{mvkGitRevDerived}:
{
  in.symbol = '@'
  in.substitutions = MVK_GIT_REV@"v$version"
  install = false
}

lib{MoltenVK}: h{mvkGitRevDerived}

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

# Apple system frameworks (package is macOS-only, see build/root.build).
# No -lobjc: Foundation/AppKit pull the runtime in. A bare -lobjc is
# rewritten to lib{objc}, and Apple ships no static archive (only a .tbd
# stub), so static consumers fail. Frameworks are passed through as-is.
#
cxx.libs += -framework Foundation -framework Metal -framework CoreGraphics \
            -framework AppKit -framework IOKit -framework IOSurface -framework QuartzCore

# Frameworks required by installed public headers (mvk_datatypes.h,
# mvk_private_api.h, mvk_deprecated_api.h).
#
lib{MoltenVK}: \
  cxx.export.libs += -framework Foundation -framework Metal \
                     -framework CoreGraphics -framework IOSurface

# 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{MoltenVK}: bin.lib.version = "-$version.project_id"
else
  lib{MoltenVK}: bin.lib.version = "-$version.major.$version.minor"

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