cmake_minimum_required(VERSION 3.22)
# Select the configuration before project() initializes compiler flags. Exported
# modules use /O2, which cannot be combined with MSVC's Debug /RTC1 flags.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build configuration" FORCE)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    message(FATAL_ERROR "The portable build kit requires an optimized configuration; use Release instead of Debug")
endif()
project(suyu_portable_aot LANGUAGES C CXX)
foreach(path_variable SUYU_CMD_RECOMP_DIR SUYU_CMD_RECOMP_PREBUILT_DIR)
    if(DEFINED ${path_variable})
        file(TO_CMAKE_PATH "${${path_variable}}" normalized_path)
        set(${path_variable} "${normalized_path}")
    endif()
endforeach()
if(CMAKE_CONFIGURATION_TYPES)
    list(REMOVE_ITEM CMAKE_CONFIGURATION_TYPES Debug)
    if(NOT CMAKE_CONFIGURATION_TYPES)
        message(FATAL_ERROR "The portable build kit requires an optimized configuration; use Release instead of Debug")
    endif()
    set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "Build configurations" FORCE)
endif()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
if(NOT WIN32 OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR
   NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
    message(FATAL_ERROR "This build kit requires a Windows x64 MSVC host toolchain")
endif()
file(READ "${CMAKE_CURRENT_LIST_DIR}/manifest.json" KIT_MANIFEST)
string(JSON KIT_PRODUCER_COMPILER_VERSION ERROR_VARIABLE KIT_COMPILER_VERSION_ERROR
    GET "${KIT_MANIFEST}" producer_compiler_version)
if(KIT_COMPILER_VERSION_ERROR OR NOT KIT_PRODUCER_COMPILER_VERSION MATCHES "^[0-9]+(\\.[0-9]+)+$")
    message(FATAL_ERROR "Build kit is missing a valid producer compiler version; download a matching current kit")
endif()
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS KIT_PRODUCER_COMPILER_VERSION)
    message(FATAL_ERROR "Update MSVC Build Tools: this build kit requires compiler version ${KIT_PRODUCER_COMPILER_VERSION} or newer; found ${CMAKE_CXX_COMPILER_VERSION}")
endif()
string(JSON KIT_INPUT_COUNT LENGTH "${KIT_MANIFEST}" inputs)
if(KIT_INPUT_COUNT LESS 1)
    message(FATAL_ERROR "Build kit contains no host link inputs")
endif()
math(EXPR KIT_LAST_INPUT "${KIT_INPUT_COUNT} - 1")
foreach(index RANGE ${KIT_LAST_INPUT})
    string(JSON input MEMBER "${KIT_MANIFEST}" inputs ${index})
    string(JSON expected GET "${KIT_MANIFEST}" inputs "${input}")
    file(SHA256 "${CMAKE_CURRENT_LIST_DIR}/${input}" actual)
    if(NOT actual STREQUAL expected)
        message(FATAL_ERROR "Corrupt or mixed build-kit input: ${input}")
    endif()
    list(APPEND KIT_LISTED_INPUTS "${input}")
endforeach()
# Escape glob characters: the kit path itself may contain [ ].
string(REPLACE "[" "[[]" KIT_GLOB_DIR "${CMAKE_CURRENT_LIST_DIR}")
string(REPLACE "*" "[*]" KIT_GLOB_DIR "${KIT_GLOB_DIR}")
string(REPLACE "?" "[?]" KIT_GLOB_DIR "${KIT_GLOB_DIR}")
file(GLOB_RECURSE KIT_PRESENT_INPUTS RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "${KIT_GLOB_DIR}/inputs/*")
foreach(present IN LISTS KIT_PRESENT_INPUTS)
    if(NOT present IN_LIST KIT_LISTED_INPUTS)
        message(FATAL_ERROR "Unlisted build-kit input: ${present}")
    endif()
endforeach()
file(READ "${CMAKE_CURRENT_LIST_DIR}/revision.txt" KIT_REVISION)
string(STRIP "${KIT_REVISION}" KIT_REVISION)
if(NOT KIT_REVISION STREQUAL "suyu-aot-kit-abi6-fm1-gg1-fpx1-control-r3" OR
   NOT SUYU_EXPORT_BUILD_KIT_REVISION STREQUAL KIT_REVISION)
    message(FATAL_ERROR "Export/build-kit revision mismatch; re-export with this release")
endif()
if(SUYU_RECOMP_HYBRID)
    include("${CMAKE_CURRENT_LIST_DIR}/hybrid.cmake")
else()
    include("${CMAKE_CURRENT_LIST_DIR}/strict.cmake")
endif()
foreach(header recomp_abi_v4.h recomp_guard_v2.h recomp_fastmem_v1.h recomp_features_v1.h recomp_guard_gen_v1.h recomp_fpx_v1.h)
    if(NOT EXISTS "${SUYU_CMD_RECOMP_DIR}/${header}")
        message(FATAL_ERROR "Missing matched ABI 6 handshake: ${header}")
    endif()
endforeach()
include("${SUYU_CMD_RECOMP_DIR}/recomp_modules.cmake")
if(NOT SUYU_RECOMP_MODULES)
    message(FATAL_ERROR "Export contains no registered modules")
endif()
set(RECOMP_STATIC_ONLY ON)
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
add_compile_definitions(BUILD_ID="amd64" _CRT_SECURE_NO_WARNINGS
    _CRT_NONSTDC_NO_DEPRECATE _SCL_SECURE_NO_WARNINGS NOMINMAX
    WIN32_LEAN_AND_MEAN UNICODE _UNICODE)
add_compile_options(/utf-8 /GS- /Gy)
if(SUYU_CMD_RECOMP_PREBUILT_DIR)
    add_library(recomp_runtime_shared STATIC IMPORTED)
    set_target_properties(recomp_runtime_shared PROPERTIES IMPORTED_LOCATION "${SUYU_CMD_RECOMP_PREBUILT_DIR}/recomp_runtime_shared.lib")
endif()
foreach(module IN LISTS SUYU_RECOMP_MODULES)
    if(NOT module MATCHES "^[A-Za-z0-9_]+$")
        message(FATAL_ERROR "Invalid module name")
    endif()
    file(READ "${SUYU_CMD_RECOMP_DIR}/${module}/recomp_runtime.h" runtime)
    if(NOT runtime MATCHES "#define RECOMP_IMAGE_ABI 6" OR NOT runtime MATCHES "tpidrro_el0")
        message(FATAL_ERROR "Module ${module} requires a fresh ABI 6 export")
    endif()
    if(SUYU_CMD_RECOMP_PREBUILT_DIR)
        add_library(recomp_static_${module} STATIC IMPORTED)
        set_target_properties(recomp_static_${module} PROPERTIES IMPORTED_LOCATION "${SUYU_CMD_RECOMP_PREBUILT_DIR}/recomp_static_${module}.lib" INTERFACE_LINK_LIBRARIES recomp_runtime_shared)
    else()
        add_subdirectory("${SUYU_CMD_RECOMP_DIR}/${module}" "${CMAKE_CURRENT_BINARY_DIR}/modules/${module}")
        target_compile_definitions(recomp_static_${module} PRIVATE recomp_image_index=recomp_image_index_${module} _recomp_index_view=_recomp_index_view_${module} recomp_build_index=recomp_build_index_${module})
    endif()
    list(APPEND module_libraries recomp_static_${module})
endforeach()
set_source_files_properties(${KIT_OBJECTS} PROPERTIES EXTERNAL_OBJECT TRUE GENERATED TRUE)
add_executable(suyu-cmd-static ${KIT_OBJECTS} "${SUYU_CMD_RECOMP_DIR}/recomp_registration.c")
set_target_properties(suyu-cmd-static PROPERTIES LINKER_LANGUAGE CXX)
target_link_options(suyu-cmd-static PRIVATE ${KIT_LINK_OPTIONS})
target_link_libraries(suyu-cmd-static PRIVATE ${module_libraries} ${KIT_LIBRARIES})
