# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Configurable variables.
# Modeled after the ndk-build system.
# For any variables defined in:
#         https://developer.android.com/ndk/guides/android_mk.html
#         https://developer.android.com/ndk/guides/application_mk.html
# if it makes sense for CMake, then replace LOCAL, APP, or NDK with ANDROID, and
# we have that variable below.
#
# ANDROID_TOOLCHAIN
# ANDROID_ABI
# ANDROID_PLATFORM
# ANDROID_STL
# ANDROID_PIE
# ANDROID_CPP_FEATURES
# ANDROID_ALLOW_UNDEFINED_SYMBOLS
# ANDROID_ARM_MODE
# ANDROID_ARM_NEON
# ANDROID_DISABLE_FORMAT_STRING_CHECKS
# ANDROID_CCACHE

cmake_minimum_required(VERSION 3.6.0)

# CMake invokes the toolchain file twice during the first build, but only once
# during subsequent rebuilds. This was causing the various flags to be added
# twice on the first build, and on a rebuild ninja would see only one set of the
# flags and rebuild the world.
# https://github.com/android-ndk/ndk/issues/323
if(ANDROID_NDK_TOOLCHAIN_INCLUDED)
  return()
endif(ANDROID_NDK_TOOLCHAIN_INCLUDED)
set(ANDROID_NDK_TOOLCHAIN_INCLUDED true)

if(DEFINED ANDROID_USE_LEGACY_TOOLCHAIN_FILE)
  set(_USE_LEGACY_TOOLCHAIN_FILE ${ANDROID_USE_LEGACY_TOOLCHAIN_FILE})
elseif(CMAKE_VERSION VERSION_LESS "3.21")
  set(_USE_LEGACY_TOOLCHAIN_FILE true)
else()
  set(_USE_LEGACY_TOOLCHAIN_FILE false)
endif()
if(_USE_LEGACY_TOOLCHAIN_FILE)
  include("${CMAKE_CURRENT_LIST_DIR}/android-legacy.toolchain.cmake")
  return()
endif()

# Android NDK path
get_filename_component(ANDROID_NDK_EXPECTED_PATH
    "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
if(NOT ANDROID_NDK)
  set(CMAKE_ANDROID_NDK "${ANDROID_NDK_EXPECTED_PATH}")
else()
  # Allow the user to specify their own NDK path, but emit a warning. This is an
  # uncommon use case, but helpful if users want to use a bleeding edge
  # toolchain file with a stable NDK.
  # https://github.com/android-ndk/ndk/issues/473
  get_filename_component(ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE)
  if(NOT "${ANDROID_NDK}" STREQUAL "${ANDROID_NDK_EXPECTED_PATH}")
    message(WARNING "Using custom NDK path (ANDROID_NDK is set): ${ANDROID_NDK}")
  endif()
  set(CMAKE_ANDROID_NDK ${ANDROID_NDK})
endif()
unset(ANDROID_NDK_EXPECTED_PATH)
file(TO_CMAKE_PATH "${CMAKE_ANDROID_NDK}" CMAKE_ANDROID_NDK)

# Android NDK revision
# Possible formats:
# * r16, build 1234: 16.0.1234
# * r16b, build 1234: 16.1.1234
# * r16 beta 1, build 1234: 16.0.1234-beta1
#
# Canary builds are not specially marked.
file(READ "${CMAKE_ANDROID_NDK}/source.properties" ANDROID_NDK_SOURCE_PROPERTIES)

set(ANDROID_NDK_REVISION_REGEX
  "^Pkg\\.Desc = Android NDK\nPkg\\.Revision = ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-beta([0-9]+))?")
if(NOT ANDROID_NDK_SOURCE_PROPERTIES MATCHES "${ANDROID_NDK_REVISION_REGEX}")
  message(SEND_ERROR "Failed to parse Android NDK revision: ${CMAKE_ANDROID_NDK}/source.properties.\n${ANDROID_NDK_SOURCE_PROPERTIES}")
endif()

set(ANDROID_NDK_MAJOR "${CMAKE_MATCH_1}")
set(ANDROID_NDK_MINOR "${CMAKE_MATCH_2}")
set(ANDROID_NDK_BUILD "${CMAKE_MATCH_3}")
set(ANDROID_NDK_BETA "${CMAKE_MATCH_5}")
if(ANDROID_NDK_BETA STREQUAL "")
  set(ANDROID_NDK_BETA "0")
endif()
set(ANDROID_NDK_REVISION
  "${ANDROID_NDK_MAJOR}.${ANDROID_NDK_MINOR}.${ANDROID_NDK_BUILD}${CMAKE_MATCH_4}")

# Touch toolchain variable to suppress "unused variable" warning.
# This happens if CMake is invoked with the same command line the second time.
if(CMAKE_TOOLCHAIN_FILE)
endif()

# Determine the ABI.
if(NOT CMAKE_ANDROID_ARCH_ABI)
  if(ANDROID_ABI STREQUAL "armeabi-v7a with NEON")
    set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
    set(CMAKE_ANDROID_ARM_NEON TRUE)
  elseif(ANDROID_ABI)
    set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI})
  elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^arm-linux-androideabi-")
    set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
  elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^aarch64-linux-android-")
    set(CMAKE_ANDROID_ARCH_ABI arm64-v8a)
  elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^x86-")
    set(CMAKE_ANDROID_ARCH_ABI x86)
  elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^x86_64-")
    set(CMAKE_ANDROID_ARCH_ABI x86_64)
  else()
    set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
  endif()
endif()
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a" AND NOT DEFINED CMAKE_ANDROID_ARM_NEON)
  if(DEFINED ANDROID_ARM_NEON)
    set(CMAKE_ANDROID_ARM_NEON ${ANDROID_ARM_NEON})
  else()
    set(CMAKE_ANDROID_ARM_NEON TRUE)
  endif()
endif()
if(NOT DEFINED CMAKE_ANDROID_ARM_MODE)
  if(DEFINED ANDROID_FORCE_ARM_BUILD)
    set(CMAKE_ANDROID_ARM_MODE ${ANDROID_FORCE_ARM_BUILD})
  elseif(DEFINED ANDROID_ARM_MODE)
    if(ANDROID_ARM_MODE STREQUAL "arm")
      set(CMAKE_ANDROID_ARM_MODE TRUE)
    elseif(ANDROID_ARM_MODE STREQUAL "thumb")
      set(CMAKE_ANDROID_ARM_MODE FALSE)
    else()
      message(FATAL_ERROR "Invalid Android ARM mode: ${ANDROID_ARM_MODE}.")
    endif()
  endif()
endif()

# PIE is supported on all currently supported Android releases, but it is not
# supported with static executables, so we still provide ANDROID_PIE as an
# escape hatch for those.
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
  if(DEFINED ANDROID_PIE)
    set(CMAKE_POSITION_INDEPENDENT_CODE ${ANDROID_PIE})
  elseif(DEFINED ANDROID_APP_PIE)
    set(CMAKE_POSITION_INDEPENDENT_CODE ${ANDROID_APP_PIE})
  else()
    set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
  endif()
endif()

# Default values for configurable variables.
if(NOT ANDROID_TOOLCHAIN)
  set(ANDROID_TOOLCHAIN clang)
elseif(ANDROID_TOOLCHAIN STREQUAL gcc)
  message(FATAL_ERROR "GCC is no longer supported. See "
  "https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.")
endif()

include(${CMAKE_ANDROID_NDK}/build/cmake/adjust_api_level.cmake)
adjust_api_level("${ANDROID_PLATFORM}" CMAKE_SYSTEM_VERSION)

if(NOT DEFINED CMAKE_ANDROID_STL_TYPE AND DEFINED ANDROID_STL)
  set(CMAKE_ANDROID_STL_TYPE ${ANDROID_STL})
endif()

if("${CMAKE_ANDROID_STL_TYPE}" STREQUAL "gnustl_shared" OR
    "${CMAKE_ANDROID_STL_TYPE}" STREQUAL "gnustl_static" OR
    "${CMAKE_ANDROID_STL_TYPE}" STREQUAL "stlport_shared" OR
    "${CMAKE_ANDROID_STL_TYPE}" STREQUAL "stlport_static")
  message(FATAL_ERROR "\
${CMAKE_ANDROID_STL_TYPE} is no longer supported. Please switch to either c++_shared \
or c++_static. See https://developer.android.com/ndk/guides/cpp-support.html \
for more information.")
endif()

# Standard cross-compiling stuff.
set(CMAKE_SYSTEM_NAME Android)

# STL.
if(ANDROID_STL)
  set(CMAKE_ANDROID_STL_TYPE ${ANDROID_STL})
endif()

if(NDK_CCACHE AND NOT ANDROID_CCACHE)
  set(ANDROID_CCACHE "${NDK_CCACHE}")
endif()
if(ANDROID_CCACHE)
  set(CMAKE_C_COMPILER_LAUNCHER   "${ANDROID_CCACHE}")
  set(CMAKE_CXX_COMPILER_LAUNCHER "${ANDROID_CCACHE}")
endif()

# Configuration specific flags.
if(ANDROID_STL_FORCE_FEATURES AND NOT DEFINED ANDROID_CPP_FEATURES)
  set(ANDROID_CPP_FEATURES "rtti exceptions")
endif()

if(ANDROID_CPP_FEATURES)
  separate_arguments(ANDROID_CPP_FEATURES)
  foreach(feature ${ANDROID_CPP_FEATURES})
    if(NOT ${feature} MATCHES "^(rtti|exceptions|no-rtti|no-exceptions)$")
      message(FATAL_ERROR "Invalid Android C++ feature: ${feature}.")
    endif()
    if(${feature} STREQUAL "rtti")
      set(CMAKE_ANDROID_RTTI TRUE)
    endif()
    if(${feature} STREQUAL "no-rtti")
      set(CMAKE_ANDROID_RTTI FALSE)
    endif()
    if(${feature} STREQUAL "exceptions")
      set(CMAKE_ANDROID_EXCEPTION TRUE)
    endif()
    if(${feature} STREQUAL "no-exceptions")
      set(CMAKE_ANDROID_EXCEPTION FALSE)
    endif()
  endforeach()
  string(REPLACE ";" " " ANDROID_CPP_FEATURES "${ANDROID_CPP_FEATURES}")
endif()

# Export configurable variables for the try_compile() command.
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
  ANDROID_ABI
  ANDROID_ALLOW_UNDEFINED_SYMBOLS
  ANDROID_ARM_MODE
  ANDROID_ARM_NEON
  ANDROID_CCACHE
  ANDROID_CPP_FEATURES
  ANDROID_DISABLE_FORMAT_STRING_CHECKS
  ANDROID_PIE
  ANDROID_PLATFORM
  ANDROID_STL
  ANDROID_TOOLCHAIN
  ANDROID_USE_LEGACY_TOOLCHAIN_FILE
)

if(DEFINED ANDROID_NO_UNDEFINED AND NOT DEFINED ANDROID_ALLOW_UNDEFINED_SYMBOLS)
  if(ANDROID_NO_UNDEFINED)
    set(ANDROID_ALLOW_UNDEFINED_SYMBOLS FALSE)
  else()
    set(ANDROID_ALLOW_UNDEFINED_SYMBOLS TRUE)
  endif()
endif()
if(DEFINED ANDROID_SO_UNDEFINED AND NOT DEFINED ANDROID_ALLOW_UNDEFINED_SYMBOLS)
  set(ANDROID_ALLOW_UNDEFINED_SYMBOLS "${ANDROID_SO_UNDEFINED}")
endif()

# Exports compatible variables defined in exports.cmake.
set(_ANDROID_EXPORT_COMPATIBILITY_VARIABLES TRUE)

if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
  set(ANDROID_HOST_TAG linux-x86_64)
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)
  set(ANDROID_HOST_TAG darwin-x86_64)
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
  set(ANDROID_HOST_TAG windows-x86_64)
endif()

# Toolchain.
set(ANDROID_TOOLCHAIN_ROOT
  "${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/${ANDROID_HOST_TAG}")

# NB: This variable causes CMake to automatically pass --sysroot to the
# toolchain. Studio currently relies on this to recognize Android builds. If
# this variable is removed, ensure that flag is still passed.
# TODO: Teach Studio to recognize Android builds based on --target.
set(CMAKE_SYSROOT "${ANDROID_TOOLCHAIN_ROOT}/sysroot")
