cmake_minimum_required(VERSION 3.0.0)

# basic build option
if(IS_IOS)
    option(USE_OPENMP       "build with openmp support"      OFF)
else()
    option(USE_OPENMP       "build with openmp support"      OFF)
endif()
option(USE_EXCEPTION    "build with exception"           ON)
option(WITH_LOGGING     "print logging for debug"        OFF)
option(WITH_SYMBOL      "build with all symbols"         ON) # turn off if use jni or ios io
option(WITH_PROFILE     "print op profile for debug"     OFF)
option(WITH_TEST        "build with unit tests"          ON)

# select platform: CPU, GPU_CL, FPGA
option(CPU              "build with arm CPU support"     ON)
option(GPU_CL           "build with OpenCL support"      ON)
option(FPGA             "build with FPGA support"        OFF)
if(FPGA)
  option(FPGAV1     "build with fpga v1 support"   ON)
  option(FPGAV2     "build with fpga v2 support"   OFF)
  option(FPGAKD     "build with fpga KD support"   OFF)
endif()

project(paddle-mobile)

# source code
file(GLOB_RECURSE PADDLE_MOBILE_CC src/*.cc src/*.cpp src/*.c src/*.mm)
file(GLOB_RECURSE PADDLE_MOBILE_H src/*.h)
include_directories(src/)

# build flags
set(CMAKE_CXX_FLAGS "-O3 -s -DNDEBUG ${CMAKE_CXX_FLAGS} -Wno-attributes")
if(IS_IOS)
    set(CMAKE_CXX_FLAGS "-mfpu=neon -marm -fobjc-abi-version=2 -fobjc-arc \
        -std=gnu++11 -stdlib=libc++ -isysroot ${CMAKE_OSX_SYSROOT} ${CMAKE_CXX_FLAGS}")
    add_compile_options(-fembed-bitcode)
else()
    set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
endif()

# others
if(USE_OPENMP)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
    add_definitions(-DPADDLE_MOBILE_USE_OPENMP)
endif()

if(WITH_LOGGING)
    message(STATUS "Debugging mode")
    add_definitions(-DPADDLE_MOBILE_DEBUG)
else()
endif()

if(NOT WITH_SYMBOL)
    add_definitions(-fvisibility=hidden -fvisibility-inlines-hidden)
endif()

if(USE_EXCEPTION)
    message(STATUS "Use exception")
    add_definitions(-DENABLE_EXCEPTION -fexceptions)
else()
    add_definitions(-fno-exceptions)
endif()

if(WITH_PROFILE)
    add_definitions(-DPADDLE_MOBILE_PROFILE)
endif()

# platform control
if(ARM_LINUX)
    include("${CMAKE_CURRENT_LIST_DIR}/tools/arm-platform.cmake")
endif()

if(CPU)
    add_definitions(-DPADDLE_MOBILE_CPU)
else()
    file(GLOB_RECURSE _tmp_list src/operators/kernel/arm/*.cpp src/operators/kernel/arm/*.cc)
    foreach(f ${_tmp_list})
        list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
    endforeach()

    file(GLOB_RECURSE _tmp_list_h src/operators/kernel/arm/*.h)
    foreach(f ${_tmp_list_h})
        list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
    endforeach()
endif()

if (GPU_CL)
    add_definitions(-DPADDLE_MOBILE_CL)

    # opencl version
    add_definitions(-DCL_TARGET_OPENCL_VERSION=220)

    if (ANDROID_ABI STREQUAL "arm64-v8a")    
        link_libraries(${CMAKE_CURRENT_LIST_DIR}/third_party/opencl/libOpenCL-64.so)
    else ()
        link_libraries(${CMAKE_CURRENT_LIST_DIR}/third_party/opencl/libOpenCL.so)
    endif ()

    include_directories(third_party/opencl/OpenCL-Headers)
else()
    file(GLOB_RECURSE _tmp_list src/framework/cl/*.cpp src/operators/kernel/cl/*.cpp)
    foreach(f ${_tmp_list})
        list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
    endforeach()

    file(GLOB_RECURSE _tmp_list_h src/framework/cl/*.h)
    foreach(f ${_tmp_list_h})
        list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
    endforeach()
endif()

if(FPGA)
    file(GLOB_RECURSE _tmp_list src/operators/math/*.cpp src/operators/math/*.cc src/operators/kernel/fpga/*.cc)
    foreach(f ${_tmp_list})
        list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
    endforeach()
    file(GLOB_RECURSE _tmp_list_h src/operators/math/*.h)
    foreach(f ${_tmp_list_h})
        list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
    endforeach()
    list(APPEND PADDLE_MOBILE_CC src/operators/math/softmax.cpp)
    list(APPEND PADDLE_MOBILE_h src/operators/math/softmax.h)
    list(APPEND PADDLE_MOBILE_h src/operators/math/math_func_neon.h)
    if(FPGAV1)
        add_definitions(-DPADDLE_MOBILE_FPGA)
        message("FPGA_V1 enabled")
        add_definitions(-DPADDLE_MOBILE_FPGA_V1)
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/V2/*.cpp src/fpga/V2/*.cpp)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/V2/*.h src/fpga/V2/*.h)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/KD/*.cpp src/fpga/KD/*.cpp)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/KD/*.h src/operators/kernel/fpga/KD/*.hpp
            src/fpga/KD/*.h src/fpga/KD/*.hpp)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
        endforeach()
    endif()
    if(FPGAV2)
        add_definitions(-DPADDLE_MOBILE_FPGA)
        message("FPGA_V2 enabled")
        add_definitions(-DPADDLE_MOBILE_FPGA_V2)
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/V1/*.cpp src/fpga/V1/*.cpp)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/V1/*.h src/fpga/V1/*.h)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/KD/*.cpp src/fpga/KD/*.cpp)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/KD/*.h src/operators/kernel/fpga/KD/*.hpp
            src/fpga/KD/*.h src/fpga/KD/*.hpp)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
        endforeach()
    endif()
    if(FPGAKD)
        message("FPGAKD enabled")
        add_definitions(-DPADDLE_MOBILE_FPGA_KD)
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/V1/*.cpp src/fpga/V1/*.cpp)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/V1/*.h src/fpga/V1/*.h)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/V2/*.cpp src/fpga/V2/*.cpp)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/V2/*.h src/fpga/V2/*.h)
        foreach(f ${_tmp_list})
            list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
        endforeach()

        file(GLOB_RECURSE _tmp_list src/operators/kernel/central-arm-func/*.h)
        foreach(f ${_tmp_list})
            list(APPEND PADDLE_MOBILE_H ${f})
        endforeach()
        file(GLOB_RECURSE _tmp_list src/operators/kernel/central-arm-func/*.cpp)
        foreach(f ${_tmp_list})
            list(APPEND PADDLE_MOBILE_CC ${f})
        endforeach()

    endif()
else()
    file(GLOB_RECURSE _tmp_list src/operators/kernel/fpga/*.cpp src/operators/kernel/fpga/*.cc)
    foreach(f ${_tmp_list})
        list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
    endforeach()

    file(GLOB_RECURSE _tmp_list_h src/operators/kernel/fpga/*.h)
    foreach(f ${_tmp_list_h})
        list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
    endforeach()


    file(GLOB_RECURSE _tmp_list src/fpga/*.cpp src/fpga/*.cc)
    foreach(f ${_tmp_list})
        list(REMOVE_ITEM PADDLE_MOBILE_CC ${f})
    endforeach()

    file(GLOB_RECURSE _tmp_list_h src/fpga/*.h)
    foreach(f ${_tmp_list_h})
        list(REMOVE_ITEM PADDLE_MOBILE_H ${f})
    endforeach()
endif()

if(ANDROID_NDK_TOOLCHAIN_INCLUDED)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -llog")
else()
    list(REMOVE_ITEM PADDLE_MOBILE_H ${CMAKE_CURRENT_SOURCE_DIR}/src/io/jni/paddle_mobile_jni.h)
    list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/io/jni/paddle_mobile_jni.cpp)
    list(REMOVE_ITEM PADDLE_MOBILE_H ${CMAKE_CURRENT_SOURCE_DIR}/src/operators/math/math_func_neon.h)
endif()

if(IS_IOS)
else()
    list(REMOVE_ITEM PADDLE_MOBILE_H ${CMAKE_CURRENT_SOURCE_DIR}/src/io/ios_io/PaddleMobileCPU.h)
    list(REMOVE_ITEM PADDLE_MOBILE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/io/ios_io/PaddleMobileCPU.mm)
    list(REMOVE_ITEM PADDLE_MOBILE_H ${CMAKE_CURRENT_SOURCE_DIR}/src/io/ios_io/op_symbols.h)
endif ()

set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY build)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY build)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY build)

# NET default
if(FPGAV1)
    set(NET "FPGA_NET_V1" CACHE STRING "select net type")
elseif(FPGAV2)
    set(NET "FPGA_NET_V2" CACHE STRING "select net type")
elseif(FPGAKD)
    set(NET "FPGA_OPS_KD" CACHE STRING "select net type")
else()
    set(NET "default"     CACHE STRING "select net type")
endif()

set_property(CACHE NET PROPERTY STRINGS "default" "googlenet" "mobilenet" "yolo" "squeezenet" "FPGA_NET_V1" "FPGA_NET_V2" "NLP" "op")
include("${CMAKE_CURRENT_LIST_DIR}/tools/op.cmake")

# build library
if(ANDROID_NDK_TOOLCHAIN_INCLUDED)
    list(REMOVE_DUPLICATES CMAKE_CXX_FLAGS)
    add_library(paddle-mobile SHARED ${PADDLE_MOBILE_CC} ${PADDLE_MOBILE_H})
elseif(IS_IOS)
    if(USE_OPENMP)
        add_library(paddle-mobile-stage0 STATIC ${PADDLE_MOBILE_CC} ${PADDLE_MOBILE_H})
        add_custom_target(paddle-mobile ALL
            COMMAND libtool -static -o ${CMAKE_BINARY_DIR}/libpaddle-mobile.a ${CMAKE_CURRENT_LIST_DIR}/tools/libomp.a $<TARGET_FILE:paddle-mobile-stage0>
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            DEPENDS paddle-mobile
        )
        add_dependencies(paddle-mobile paddle-mobile-stage0)
    else()
        add_library(paddle-mobile STATIC ${PADDLE_MOBILE_CC} ${PADDLE_MOBILE_H})
    endif()
else()
  add_library(paddle-mobile SHARED ${PADDLE_MOBILE_CC} ${PADDLE_MOBILE_H})
endif()

# unit test
if(WITH_TEST AND WITH_SYMBOL)
    if(IS_IOS)
    else()
        add_subdirectory(test)
    endif()
elseif(FPGA)
    add_subdirectory(test)
endif()

# # if you want to combine third party static librares into paddle mobile so, please uncomment this code block
# target_link_libraries(
#     paddle-mobile
#     -Wl,--whole-archive
#     "path_to_third_party_static_library"
#     -Wl,--no-whole-archive
# )
