# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
# Copyright 2026 Arm Limited and/or its affiliates.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
add_subdirectory(json)
# hexagon toolchain does not support mkdir, which is used in gflags.
if(NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES Hexagon)
  add_subdirectory(gflags)
endif()

if(EXECUTORCH_BUILD_PYBIND)
  add_subdirectory(pybind11)
endif()

if(BUILD_TESTING)
  add_subdirectory(googletest)
endif()

# MARK: - flatbuffers

if(WIN32)
  # On Windows with the Visual Studio generator, unsetting CMAKE_TOOLCHAIN_FILE is
  # not sufficient to force a host (AMD64) build when cross-compiling for ARM64.
  # The VS generator propagates the parent's platform (-A ARM64) to child
  # ExternalProjects automatically.  CMAKE_GENERATOR_PLATFORM overrides it, but
  # CMake requires CMAKE_GENERATOR to be set explicitly alongside it.  We pass
  # the parent's generator string verbatim so the child uses the same VS version
  # but targets x64 (the host) and produces a runnable flatc.exe / flatcc.exe.
  if(CMAKE_CROSSCOMPILING AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "ARM64|arm64|aarch64")
    set(_flatbuffers_ep_additional_args
        CMAKE_GENERATOR "${CMAKE_GENERATOR}"
        CMAKE_GENERATOR_PLATFORM x64)
  else()
    set(_flatbuffers_ep_additional_args)
  endif()
  set(_executorch_external_project_additional_args)
else()
  # Always use Make to avoid needing to codesign flatc if the project is using
  # Xcode.
  set(_executorch_external_project_additional_args CMAKE_GENERATOR
                                                   "Unix Makefiles"
  )
  set(_flatbuffers_ep_additional_args)
endif()

# The flatc and flatcc host tools are built for the build machine. On a non-MAC
# Apple build the parent's CMAKE_OSX_DEPLOYMENT_TARGET is an iOS floor the host
# compiler rejects, so the host sub-builds get this macOS floor instead, to
# match the package's macOS minimum. _executorch_apple_cross is true only for a
# non-MAC Apple build, where the host tool must not take the app's SDK or floor.
set(_executorch_host_macos_floor 14.0)
set(_executorch_apple_cross
    "$<AND:$<BOOL:${APPLE}>,$<BOOL:$<FILTER:${PLATFORM},EXCLUDE,^MAC>>>"
)
set(_executorch_host_osx_args
    "$<${_executorch_apple_cross}:-DCMAKE_OSX_SYSROOT=>"
    "-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=$<IF:${_executorch_apple_cross},${_executorch_host_macos_floor},${CMAKE_OSX_DEPLOYMENT_TARGET}>"
)
# The host's executable suffix, for the byproducts and the imported locations
# alike. Not CMAKE_EXECUTABLE_SUFFIX, which is the target's: a cross toolchain
# such as Zephyr's sets .elf, and a byproduct named after it is not the file the
# host build writes, so Ninja would have no rule for the tool.
if(CMAKE_HOST_WIN32)
  set(_executorch_host_executable_suffix ".exe")
else()
  set(_executorch_host_executable_suffix "")
endif()

# Allow reusing a prebuilt host flatc instead of building it from source. This is
# required when cross-compiling with the Ninja generator on Windows: the WIN32
# branch above only supports the Visual Studio generator (it passes the VS-only
# CMAKE_GENERATOR_PLATFORM), and the flatbuffers_ep host sub-build otherwise
# fails under Ninja. Pass -DFLATC_EXECUTABLE=<path to a host flatc(.exe)>.
if(FLATC_EXECUTABLE)
  add_executable(flatc IMPORTED GLOBAL)
  set_target_properties(flatc PROPERTIES IMPORTED_LOCATION "${FLATC_EXECUTABLE}")
else()
# We use ExternalProject to build flatc from source to force it target the host.
# Otherwise, flatc will target the project's toolchain (i.e. iOS, or Android).
ExternalProject_Add(
  flatbuffers_ep
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/flatc_ep
  BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/flatc_ep/src/build
  SOURCE_DIR ${PROJECT_SOURCE_DIR}/third-party/flatbuffers
  CMAKE_ARGS
    -DFLATBUFFERS_BUILD_FLATC=ON
    -DFLATBUFFERS_INSTALL=ON
    -DFLATBUFFERS_BUILD_FLATHASH=OFF
    -DFLATBUFFERS_BUILD_FLATLIB=OFF
    -DFLATBUFFERS_BUILD_TESTS=OFF
    -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
    -DCMAKE_CXX_FLAGS="-DFLATBUFFERS_MAX_ALIGNMENT=${EXECUTORCH_FLATBUFFERS_MAX_ALIGNMENT}"
    # Unset the toolchain to build for the host instead of the toolchain set for
    # the project.
    -DCMAKE_TOOLCHAIN_FILE=
    # flatc runs on the build machine, so it must target the host, not the app's
    # iOS platform. See _executorch_host_osx_args above.
    ${_executorch_host_osx_args}
  # With the suffix, or Ninja on Windows has no rule producing the flatc.exe
  # that the generated schema headers depend on.
  BUILD_BYPRODUCTS <INSTALL_DIR>/bin/flatc${_executorch_host_executable_suffix}
                   ${_executorch_external_project_additional_args}
                   ${_flatbuffers_ep_additional_args}
)
ExternalProject_Get_Property(flatbuffers_ep INSTALL_DIR)
add_executable(flatc IMPORTED GLOBAL)
add_dependencies(flatc flatbuffers_ep)
# flatbuffers does not use CMAKE_BUILD_TYPE. Internally, the build forces Release
# config, but from CMake's perspective the build type is always Debug.
set_target_properties(
  flatc PROPERTIES IMPORTED_LOCATION
                   ${INSTALL_DIR}/bin/flatc${_executorch_host_executable_suffix}
)
endif()

# TODO: re-enable once flatbuffers is added as a subdirectory.
# set(FLATBUFFERS_BUILD_FLATC OFF) set(FLATBUFFERS_INSTALL OFF)
# set(FLATBUFFERS_BUILD_FLATHASH OFF) set(FLATBUFFERS_BUILD_FLATLIB OFF)
# set(FLATBUFFERS_BUILD_TESTS OFF)

# MARK: - flatcc

if(WIN32)
  # For some reason, when configuring the external project during build
  # CMAKE_C_SIMULATE_ID is set to MSVC, but CMAKE_CXX_SIMULATE_ID is not set. To
  # make sure the external project is configured correctly, set it explicitly
  # here.
  set(_flatcc_extra_cmake_args -DCMAKE_CXX_SIMULATE_ID=MSVC)
else()
  set(_flatcc_extra_cmake_args)
endif()

# Allow reusing a prebuilt host flatcc (see the flatc note above). Needed for the
# Ninja-on-Windows cross-compile, where the flatcc_ep host sub-build fails. Pass
# -DFLATCC_EXECUTABLE=<path to a host flatcc(.exe)>. flatcc_cli is only consumed
# by devtools/etdump, so this imported target is unused when devtools is off.
if(FLATCC_EXECUTABLE)
  add_executable(flatcc_cli IMPORTED GLOBAL)
  set_target_properties(flatcc_cli PROPERTIES IMPORTED_LOCATION "${FLATCC_EXECUTABLE}")
else()
# Similar to flatbuffers, we want to build flatcc for the host. See inline
# comments in the flatbuffers ExternalProject_Add for more details.
ExternalProject_Add(
  flatcc_ep
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/flatcc_ep
  SOURCE_DIR ${PROJECT_SOURCE_DIR}/third-party/flatcc-host
  BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/flatcc_ep/src/host-build
  BUILD_ALWAYS TRUE
  CMAKE_ARGS
    -DEXECUTORCH_FLATCC_SOURCE_DIR:PATH=${PROJECT_SOURCE_DIR}/third-party/flatcc
    -DFLATCC_RTONLY=OFF
    -DFLATCC_TEST=OFF
    -DFLATCC_REFLECTION=OFF
    -DFLATCC_DEBUG_CLANG_SANITIZE=OFF
    -DFLATCC_ALLOW_WERROR=OFF
    -DFLATCC_INSTALL=ON
    -DCMAKE_POLICY_VERSION_MINIMUM=3.5
    -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
    -DCMAKE_POSITION_INDEPENDENT_CODE=ON
    -DCMAKE_TOOLCHAIN_FILE=
    ${_executorch_host_osx_args}
    ${_flatcc_extra_cmake_args}
  BUILD_BYPRODUCTS <INSTALL_DIR>/bin/flatcc${_executorch_host_executable_suffix}
                   ${_executorch_external_project_additional_args}
                   ${_flatbuffers_ep_additional_args}
)
ExternalProject_Get_Property(flatcc_ep INSTALL_DIR)
add_executable(flatcc_cli IMPORTED GLOBAL)
add_dependencies(flatcc_cli flatcc_ep)
set_target_properties(
  flatcc_cli
  PROPERTIES IMPORTED_LOCATION
             ${INSTALL_DIR}/bin/flatcc${_executorch_host_executable_suffix}
)
endif()

set(FLATCC_RTONLY
    ON
    CACHE BOOL ""
)
set(FLATCC_TEST
    OFF
    CACHE BOOL ""
)
set(FLATCC_REFLECTION
    OFF
    CACHE BOOL ""
)
set(FLATCC_DEBUG_CLANG_SANITIZE
    OFF
    CACHE BOOL ""
)
set(FLATCC_ALLOW_WERROR
    OFF
    CACHE BOOL ""
)
set(FLATCC_INSTALL
    OFF
    CACHE BOOL ""
)
add_subdirectory(flatcc)
set_target_properties(
  flatccrt
  PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/flatcc/lib"
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/flatcc/lib"
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/flatcc/bin"
)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU"
   AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15
)
  target_compile_options(
    flatccrt PRIVATE -Wno-error=unterminated-string-initialization
  )
endif()
# Fix for "relocation R_X86_64_32 against `.rodata' can not be used when making
# a shared object; recompile with -fPIC" when building on some x86 linux
# systems.
#
# Learn more: https://github.com/pytorch/executorch/pull/2467
set_property(TARGET flatccrt PROPERTY POSITION_INDEPENDENT_CODE ON)
install(TARGETS flatccrt DESTINATION ${CMAKE_INSTALL_LIBDIR})
