# Cross-compile joyinside for Luckfox Aura (Debian 13, aarch64/glibc).

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

if(NOT DEFINED ENV{JOYINSIDE_SYSROOT})
    message(FATAL_ERROR
        "JOYINSIDE_SYSROOT is not set.\n"
        "Example: export JOYINSIDE_SYSROOT=$HOME/rk_sdk/debian_sysroot")
endif()

set(SYSROOT "$ENV{JOYINSIDE_SYSROOT}")

set(_curl_h_legacy "${SYSROOT}/usr/include/curl/curl.h")
set(_curl_h_multiarch "${SYSROOT}/usr/include/aarch64-linux-gnu/curl/curl.h")
if(NOT EXISTS "${SYSROOT}/usr/include/openssl/ssl.h")
    message(FATAL_ERROR
        "Invalid sysroot: ${SYSROOT}\n"
        "Missing usr/include/openssl/ssl.h — copy libssl-dev headers into the sysroot.")
endif()
if(NOT EXISTS "${_curl_h_legacy}" AND NOT EXISTS "${_curl_h_multiarch}")
    message(FATAL_ERROR
        "Invalid sysroot: ${SYSROOT}\n"
        "Missing curl.h — copy libcurl4-openssl-dev headers into the sysroot.")
endif()

set(_pthread_h "${SYSROOT}/usr/include/pthread.h")
if(NOT EXISTS "${_pthread_h}")
    set(_pthread_h "${SYSROOT}/usr/include/aarch64-linux-gnu/pthread.h")
endif()
if(NOT EXISTS "${_pthread_h}")
    message(FATAL_ERROR
        "Invalid sysroot: ${SYSROOT}\n"
        "Missing pthread.h — copy libc6-dev usr/include/pthread.h into the sysroot.")
endif()

set(CMAKE_SYSROOT "${SYSROOT}")
set(CMAKE_FIND_ROOT_PATH
    "${SYSROOT}"
    "${SYSROOT}/usr"
    "${SYSROOT}/usr/lib/aarch64-linux-gnu"
    "${SYSROOT}/lib/aarch64-linux-gnu"
    "${SYSROOT}/usr/include/aarch64-linux-gnu")

# Determine the cross compiler early: the include-path setup below needs to query
# the toolchain for its own bundled sysroot.
if(DEFINED ENV{JOYINSIDE_CROSS_PREFIX})
    set(CROSS_PREFIX "$ENV{JOYINSIDE_CROSS_PREFIX}")
elseif(DEFINED ENV{JOYINSIDE_SDK})
    set(CROSS_PREFIX "aarch64-rockchip1240-linux-gnu")
else()
    set(CROSS_PREFIX "aarch64-linux-gnu")
endif()

if(DEFINED ENV{JOYINSIDE_TOOLCHAIN_BIN})
    set(TOOLCHAIN_BIN "$ENV{JOYINSIDE_TOOLCHAIN_BIN}")
    set(CMAKE_C_COMPILER "${TOOLCHAIN_BIN}/${CROSS_PREFIX}-gcc")
    set(CMAKE_CXX_COMPILER "${TOOLCHAIN_BIN}/${CROSS_PREFIX}-g++")
else()
    set(CMAKE_C_COMPILER "${CROSS_PREFIX}-gcc")
    set(CMAKE_CXX_COMPILER "${CROSS_PREFIX}-g++")
endif()

message(STATUS "Cross C compiler:   ${CMAKE_C_COMPILER}")
message(STATUS "Cross C++ compiler: ${CMAKE_CXX_COMPILER}")
message(STATUS "Sysroot: ${SYSROOT}")

# Both Debian include dirs MUST be -isystem, arch-specific FIRST:
#   1) usr/include/aarch64-linux-gnu  -> asm/*.h, bits/*.h (pthread_cond_t, ...)
#   2) usr/include                    -> pthread.h, stdio.h, features.h, ...
#
# They have to out-prioritize the toolchain's own bundled libc headers. This
# Rockchip GCC ships a fixincludes copy of pthread.h at
#   lib/gcc/<target>/12.4.0/include-fixed/pthread.h
# (old glibc) which sits ABOVE the sysroot in the default search order. If the
# generic dir were only -idirafter (lowest priority), <pthread.h> would resolve
# to that old copy while <bits/*> come from the new Debian glibc -> mixed glibc:
# PTHREAD_COND_INITIALIZER (old, flat braces) vs pthread_cond_t (new, nested) ->
# "cannot convert {...} to unsigned int" in <mutex>/<condition_variable>.
# Keeping the arch dir first means <asm/*.h> and <linux/*.h> -> <asm/*.h> still
# resolve to the Debian arch headers.
set(_arch_inc "${SYSROOT}/usr/include/aarch64-linux-gnu")
set(_generic_inc "${SYSROOT}/usr/include")
if(NOT EXISTS "${_arch_inc}/asm/types.h")
    message(FATAL_ERROR
        "Invalid sysroot: missing ${_arch_inc}/asm/types.h\n"
        "Copy usr/include/aarch64-linux-gnu and usr/lib/linux from the board sysroot.")
endif()
set(CMAKE_SYSTEM_INCLUDE_PATH "${_arch_inc};${_generic_inc};${CMAKE_SYSTEM_INCLUDE_PATH}")
set(_inc_flags "-isystem ${_arch_inc}")
if(IS_DIRECTORY "${_generic_inc}")
    set(_inc_flags "${_inc_flags} -isystem ${_generic_inc}")
endif()

# The whole libc MUST come from the Debian sysroot so that the generic headers
# (features.h, stdio.h, pthread.h, ...) and the arch-specific bits/ stay on the
# SAME glibc version. Mixing the toolchain's bundled glibc generic headers with
# the Debian arch bits silently breaks ABI-sensitive types (e.g. pthread_cond_t
# vs PTHREAD_COND_INITIALIZER -> "cannot convert {...} to unsigned int" in
# <mutex>/<condition_variable>). So we do NOT fall back to the toolchain headers:
# if the sysroot is incomplete, fail loudly and say exactly what to sync.
set(_required_generic_headers
    stdio.h stdlib.h string.h features.h time.h unistd.h errno.h
    sched.h signal.h pthread.h)
set(_missing_generic_headers "")
foreach(_hdr IN LISTS _required_generic_headers)
    if(NOT EXISTS "${_generic_inc}/${_hdr}")
        list(APPEND _missing_generic_headers "${_hdr}")
    endif()
endforeach()
if(_missing_generic_headers)
    string(REPLACE ";" " " _missing_str "${_missing_generic_headers}")
    message(FATAL_ERROR
        "Incomplete Debian sysroot: ${SYSROOT}\n"
        "Missing generic libc headers in usr/include: ${_missing_str}\n"
        "The arch bits (usr/include/aarch64-linux-gnu) are present but the generic\n"
        "glibc headers are not, so the libc would be inconsistent. Sync the full\n"
        "usr/include tree from the target board (run on the build host):\n"
        "  rsync -aL root@<board-ip>:/usr/include/ ${SYSROOT}/usr/include/\n")
endif()
set(CMAKE_C_FLAGS "${_inc_flags}" CACHE STRING "Cross C include paths" FORCE)
set(CMAKE_CXX_FLAGS "${_inc_flags}" CACHE STRING "Cross C++ include paths" FORCE)
# Make try_compile / CHECK_* probes use the SAME include ordering as the real build,
# so a header that compiles in a probe also compiles in the project (and vice versa).
set(CMAKE_REQUIRED_FLAGS "${_inc_flags}" CACHE STRING "" FORCE)
# If any subproject still calls find_package(Threads), make it prefer -pthread
# instead of trying to locate libpthread inside the sysroot.
set(THREADS_PREFER_PTHREAD_FLAG ON CACHE BOOL "Use -pthread for Threads" FORCE)

# Luckfox/Rockchip gcc + Debian sysroot: ld does not pick up crt*.o from sysroot
# unless startup (-B) and library (-L) search paths are set explicitly.
set(CRT_LIB_DIR "")
foreach(_libdir
    "${SYSROOT}/usr/lib/aarch64-linux-gnu"
    "${SYSROOT}/lib/aarch64-linux-gnu"
    "${SYSROOT}/usr/lib"
    "${SYSROOT}/lib")
    if(EXISTS "${_libdir}/crt1.o"
       AND EXISTS "${_libdir}/crti.o"
       AND EXISTS "${_libdir}/crtn.o")
        set(CRT_LIB_DIR "${_libdir}")
        break()
    endif()
endforeach()
if(NOT CRT_LIB_DIR)
    message(FATAL_ERROR
        "crt1.o/crti.o/crtn.o not found under ${SYSROOT}.\n"
        "Copy libc6-dev runtime objects (usr/lib/aarch64-linux-gnu/crt*.o) into the sysroot.")
endif()

set(_link_lib_dirs "")
foreach(_libdir
    "${SYSROOT}/usr/lib/aarch64-linux-gnu"
    "${SYSROOT}/lib/aarch64-linux-gnu")
    if(IS_DIRECTORY "${_libdir}")
        list(APPEND _link_lib_dirs "${_libdir}")
    endif()
endforeach()

set(_crt_link_flags "-B${CRT_LIB_DIR}/ -L${CRT_LIB_DIR}")
foreach(_libdir IN LISTS _link_lib_dirs)
    if(NOT _libdir STREQUAL CRT_LIB_DIR)
        set(_crt_link_flags "${_crt_link_flags} -L${_libdir}")
    endif()
endforeach()

set(_sysroot_link "-Wl,--sysroot=${SYSROOT}")
set(_rpath_flags "-Wl,-rpath-link,${SYSROOT}/usr/lib/aarch64-linux-gnu:${SYSROOT}/lib/aarch64-linux-gnu")

# Rockchip gcc defaults to /lib/ld-linux-aarch64.so.1 as PT_INTERP, but Debian
# multiarch sysroots ship the file under /lib/aarch64-linux-gnu/. Without an
# explicit --dynamic-linker, ld.bfd looks on the build host and fails when
# linking executables (joyinside_demo, vendored test binaries).
set(_ld_interp "")
foreach(_ldpath
    "/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1"
    "/lib/ld-linux-aarch64.so.1")
    if(EXISTS "${SYSROOT}${_ldpath}")
        set(_ld_interp "${_ldpath}")
        break()
    endif()
endforeach()
if(NOT _ld_interp)
    message(FATAL_ERROR
        "ld-linux-aarch64.so.1 not found under ${SYSROOT}/lib.\n"
        "Copy the dynamic linker from the board, e.g.:\n"
        "  rsync -aL root@<board-ip>:/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 \\\n"
        "    ${SYSROOT}/lib/aarch64-linux-gnu/")
endif()

# Rockchip ld.bfd still probes the toolchain default /lib/ld-linux-aarch64.so.1
# (with --sysroot) even when PT_INTERP is set to the Debian multiarch path.
# Debian sysroots often ship only lib/aarch64-linux-gnu/ld-linux-aarch64.so.1.
set(_ld_legacy "${SYSROOT}/lib/ld-linux-aarch64.so.1")
set(_ld_multiarch "${SYSROOT}/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1")
if(EXISTS "${_ld_multiarch}" AND NOT EXISTS "${_ld_legacy}")
    file(MAKE_DIRECTORY "${SYSROOT}/lib")
    file(CREATE_LINK "aarch64-linux-gnu/ld-linux-aarch64.so.1" "${_ld_legacy}" SYMBOLIC)
    message(STATUS "Sysroot: created lib/ld-linux-aarch64.so.1 -> aarch64-linux-gnu/ (for ld.bfd)")
endif()

set(_link_flags
    "${_crt_link_flags} ${_sysroot_link} -Wl,--dynamic-linker=${_ld_interp} ${_rpath_flags}")

# CACHE FORCE: CMAKE_*_INIT alone is not always passed to the real link step
# after CMake finishes its compiler checks.
set(CMAKE_EXE_LINKER_FLAGS "${_link_flags}" CACHE STRING "Cross exe link flags" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS "${_link_flags}" CACHE STRING "Cross shared link flags" FORCE)

# Skip linking during compiler ABI probe; full project links with linker flags above.
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

message(STATUS "C runtime (crt): ${CRT_LIB_DIR}")
message(STATUS "Dynamic linker: ${_ld_interp}")
message(STATUS "Link flags: ${_link_flags}")

# Help FindOpenSSL / FindCURL / FindALSA in Debian multiarch sysroots.
set(OPENSSL_ROOT_DIR "${SYSROOT}/usr" CACHE PATH "" FORCE)
set(CURL_ROOT "${SYSROOT}/usr" CACHE PATH "" FORCE)
if(EXISTS "${SYSROOT}/usr/include/aarch64-linux-gnu/curl/curl.h")
    set(CURL_INCLUDE_DIR "${SYSROOT}/usr/include/aarch64-linux-gnu" CACHE PATH "" FORCE)
else()
    set(CURL_INCLUDE_DIR "${SYSROOT}/usr/include" CACHE PATH "" FORCE)
endif()

# FindALSA finds headers (version 1.2.14) but often misses libasound in ONLY mode
# without an explicit path — library lives under usr/lib/aarch64-linux-gnu.
set(_alsa_inc "")
foreach(_inc
    "${SYSROOT}/usr/include/aarch64-linux-gnu"
    "${SYSROOT}/usr/include")
    if(EXISTS "${_inc}/alsa/asoundlib.h")
        set(_alsa_inc "${_inc}")
        break()
    endif()
endforeach()

set(_alsa_lib "")
foreach(_libdir IN LISTS _link_lib_dirs)
    foreach(_soname IN ITEMS libasound.so libasound.so.2)
        if(EXISTS "${_libdir}/${_soname}")
            set(_alsa_lib "${_libdir}/${_soname}")
            break()
        endif()
    endforeach()
    if(_alsa_lib)
        break()
    endif()
endforeach()

if(_alsa_inc)
    set(ALSA_INCLUDE_DIR "${_alsa_inc}" CACHE PATH "ALSA headers (cross sysroot)" FORCE)
endif()
if(_alsa_lib)
    set(ALSA_LIBRARY "${_alsa_lib}" CACHE FILEPATH "libasound (cross sysroot)" FORCE)
endif()

# Debian multiarch: libcurl headers live under usr/include/aarch64-linux-gnu/curl/
set(ENV{PKG_CONFIG_SYSROOT_DIR} "${SYSROOT}")
set(ENV{PKG_CONFIG_PATH} "${SYSROOT}/usr/lib/aarch64-linux-gnu/pkgconfig")
set(ENV{PKG_CONFIG_LIBDIR} "${SYSROOT}/usr/lib/aarch64-linux-gnu/pkgconfig")

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
