# This file is part of the mlhpbf project. License: See LICENSE
#
# Example of building a C++ extension module against a pip installed pbf, which doubles as the
# test that a wheel is a complete dependency: headers, the mlhp it was compiled against, and a
# pybind11 type registry shared with pbf's and mlhp's own modules.
#
# Shipped inside the wheel as well, so users obtain these files against their own installation
# with pbf.example( "cpp_extension", path="<directory>" ). See the docstring of
# extension_test.py for what the example does.
#
# Configure aborts with the pybind11 version to install if it does not match pbf's.
#
# The module exchanges C++ objects with the pbf and mlhp binaries, so it needs a toolchain ABI
# compatible with the one that built the wheels: MSVC on Windows (toolsets v140 to v143 are
# mutually compatible, MinGW is not), libstdc++ on linux with a gcc no older than the manylinux
# image's, and apple clang with libc++ on macOS. The language standard is not a separate
# concern - mlhp::core carries cxx_std_20 as an interface compile feature, so cmake applies it.
#
#   pip install pbf pybind11
#   cmake -S <this directory> -B <builddir>
#   cmake --build <builddir> --config Release
#   ctest --test-dir <builddir> -C Release --output-on-failure

# 3.19 for COMMAND_ERROR_IS_FATAL and Development.Module below
cmake_minimum_required( VERSION 3.19 )

project( pbf_extension LANGUAGES CXX )

# Both components: pybind11 finds Python again below and a cached interpreter-only result
# leaves python_add_library undefined.
find_package( Python COMPONENTS Interpreter Development.Module REQUIRED )

# Without this pybind11 uses its classic finder, which picks whichever interpreter comes
# first on PATH rather than the one given in Python_EXECUTABLE. Building against a different
# python than pbf was built for fails only later, when both modules are imported together.
if( NOT DEFINED PYBIND11_FINDPYTHON )
    set( PYBIND11_FINDPYTHON NEW )
endif( )

# All three ship their cmake package inside the python distribution
foreach( package pbf mlhp pybind11 )
    if( NOT DEFINED ${package}_DIR )
        execute_process( COMMAND ${Python_EXECUTABLE} -c
                         "import ${package}; print(${package}.get_cmake_dir())"
                         OUTPUT_VARIABLE ${package}_DIR
                         OUTPUT_STRIP_TRAILING_WHITESPACE
                         COMMAND_ERROR_IS_FATAL ANY )
    endif( )
endforeach( )

# Pulls in mlhp as an exact dependency of pbf
find_package( pbf CONFIG REQUIRED )
find_package( pybind11 CONFIG REQUIRED )

# Stricter than the internals version that governs compatibility, so that a bumped submodule
# is noticed here rather than in a downstream project.
if( mlhp_PYBIND11_VERSION AND NOT pybind11_VERSION VERSION_EQUAL mlhp_PYBIND11_VERSION )
    message( FATAL_ERROR "Found pybind11 ${pybind11_VERSION}, but this pbf was built "
                         "against ${mlhp_PYBIND11_VERSION}. Install the matching one with "
                         "\"pip install pybind11==${mlhp_PYBIND11_VERSION}\"." )
endif( )

pybind11_add_module( pbf_extension extension.cpp )

target_link_libraries( pbf_extension PRIVATE mlhp::pbf )

# A test so the per-config module subdirectory does not have to be spelled out
enable_testing( )

add_test( NAME extension COMMAND ${Python_EXECUTABLE} -X faulthandler
          ${CMAKE_CURRENT_SOURCE_DIR}/extension_test.py $<TARGET_FILE_DIR:pbf_extension> )
