#===============================================================================
# Copyright 2019-2024 Intel Corporation
#
# 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.
#===============================================================================

cmake_minimum_required(VERSION 3.13)

if("${CMAKE_BUILD_TYPE}" STREQUAL "")
    message(STATUS "CMAKE_BUILD_TYPE is unset, defaulting to Release")
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING
        "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ...")
endif()
string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPERCASE_CMAKE_BUILD_TYPE)

project (DNNL_EXAMPLES)

set(DNNL_CPU_RUNTIME "@DNNL_CPU_RUNTIME@")
set(DNNL_GPU_RUNTIME "@DNNL_GPU_RUNTIME@")

set(DNNL_INSTALL_MODE "@DNNL_INSTALL_MODE@")
set(IS_NEW_DIR_LAYOUT FALSE)
if(DNNL_INSTALL_MODE STREQUAL "BUNDLE_V2")
    set(IS_NEW_DIR_LAYOUT TRUE)
endif()

if(NOT DEFINED DNNLROOT AND DEFINED ENV{DNNLROOT})
    set(DNNLROOT "$ENV{DNNLROOT}" CACHE STRING "")
else()
    if(IS_NEW_DIR_LAYOUT)
        set(DNNLROOT "${PROJECT_SOURCE_DIR}/../../../.." CACHE STRING "")
    else()
        set(DNNLROOT "${PROJECT_SOURCE_DIR}/.." CACHE STRING "")
    endif()
endif()

message(STATUS "DNNLROOT: ${DNNLROOT}")

if(IS_NEW_DIR_LAYOUT)
    include_directories(${DNNLROOT}/share/doc/@LIB_PACKAGE_NAME@/examples)
else()
    include_directories(${DNNLROOT}/examples)
endif()

enable_testing()

if(DNNL_CPU_RUNTIME MATCHES "(SYCL|DPCPP)" OR DNNL_GPU_RUNTIME MATCHES "(SYCL|DPCPP)")
    set(DNNL_WITH_SYCL true)
else()
    set(DNNL_WITH_SYCL false)
endif()

if(DNNL_WITH_SYCL)
    include("dpcpp_driver_check.cmake")
endif()

if(CMAKE_BASE_NAME MATCHES "^(icx|icpx)$")
    # icx/icpx may issue a recommendation warning on using
    # -qopenmp over -fopenmp to enable some additional optimizations.
    # Suppress the warning to avoid breaking the build until we figure out
    # whether it makes sense to enable those optimizations.
    # Also, suppress a warning about unknown options as the older compiler
    # versions may not support "-Wno-recommended-option".
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-recommended-option -Wno-unknown-warning-option")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-recommended-option -Wno-unknown-warning-option")
endif()

if(UNIX OR MINGW)
    find_library(LIBM m REQUIRED)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")

    if(NOT DNNL_WITH_SYCL)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
    endif()

    if(NOT APPLE)
        set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed")
    endif()
    # XXX: DPC++ compiler generates a lot of warnings
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
endif()

if(${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
    add_definitions(/Qpar)
    add_definitions(/openmp)
else()
    find_package(OpenMP)
    if(OpenMP_C_FOUND)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    endif()
    if(OpenMP_CXX_FOUND)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
    endif()
endif()

if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL "DEBUG" AND NOT APPLE)
    if(${CMAKE_CXX_COMPILER_ID} MATCHES "IntelLLVM" OR CMAKE_BASE_NAME MATCHES "(icx|icpx|dpcpp)" OR DNNL_WITH_SYCL)
        # When using Debug build mode CMake adds '-debug' or '-g' without
        # any optimization-level option that will turn off most compiler
        # optimizations similar to use of '-Od' or '-O0'.
        # Here we disable the warning and remark aboiut this issue.
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-debug-disables-optimization -Rno-debug-disables-optimization")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-debug-disables-optimization -Rno-debug-disables-optimization")
        if(WIN32)
            # Disabling OMP SIMD feature on Windows ICX debug builds and
            # suppressing the warning: Using /MTd or /MDd with '#pragma omp simd'
            # may lead to unexpected fails due to the debugging version of iterators
            # that cannot be vectorized correctly.
            set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Qopenmp-simd- -Wno-debug-option-simd")
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qopenmp-simd- -Wno-debug-option-simd")
        endif()
    endif()
endif()

if(WIN32 AND DNNL_WITH_SYCL)
    # XXX: SYCL does not like __thiscall convention coming from TBB,
    # suppress warnings for now.
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ignored-attributes")

    # XXX: compiler always pulls in release C++ runtime by default, until
    # this is fixed we have to explicitly drop release C++ runtime for
    # debug build types.
    if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL "DEBUG" AND NOT CMAKE_BASE_NAME MATCHES "(icx|dpcpp)")
        set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Xlinker /NODEFAULTLIB:msvcrt")
    endif()
endif()

if(NOT IS_NEW_DIR_LAYOUT)
    # DNNL_CONFIGURATION is only used by oneAPI CMake config and ignored otherwise.
    get_filename_component(DNNL_CONFIGURATION "${DNNLROOT}" ABSOLUTE)
    get_filename_component(DNNL_CONFIGURATION "${DNNL_CONFIGURATION}" NAME)
    # The hint can be used to find CMake config in oneAPI directory layout.
    set(ONEAPI_CMAKE_CONFIG_HINT "../..")
endif()

find_package(@LIB_PACKAGE_NAME@ @PROJECT_VERSION@ CONFIG REQUIRED HINTS ${DNNLROOT} ${ONEAPI_CMAKE_CONFIG_HINT})

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DNNL_COMPILE_FLAGS}")

if(WIN32)
    set(CTESTCONFIG_PATH "${DNNLROOT}\\bin")
elseif(APPLE)
    set(CTESTCONFIG_PATH "${DNNLROOT}/lib")
endif()

# Configuration for tests / test cases on Windows
function(maybe_configure_test name kind)
    if(WIN32)
        string(REPLACE  ";" "\;" PATH "${CTESTCONFIG_PATH};$ENV{PATH}")
        set_property(${kind} ${name} PROPERTY ENVIRONMENT "PATH=${PATH}")
        if(CMAKE_GENERATOR MATCHES "Visual Studio")
            configure_file(template.vcxproj.user ${name}.vcxproj.user @ONLY)
        endif()
    endif()
endfunction()

# Register example
#   name -- name of the executable
#   srcs -- list of source, if many must be enclosed with ""
#   test -- "test" to mark executable as a test, "" otherwise
function(register_example name srcs test)
    add_executable(${name} ${srcs})
    string(TOUPPER "@LIB_PACKAGE_NAME@" LIB_NAMESPACE)
    target_link_libraries(${name} ${LIB_NAMESPACE}::@LIB_PACKAGE_NAME@ ${LIBM})
    if("x${test}" STREQUAL "xtest")
        add_test(${name} ${name})
        maybe_configure_test("${example_name}" TEST)
    endif()
endfunction()

file(GLOB sources *.cpp *.c)
file(GLOB_RECURSE tutorial_sources tutorials/*.cpp tutorials/*.c)
list(APPEND sources ${tutorial_sources})

foreach(src ${sources})
    file(RELATIVE_PATH src_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${src})
    string(REGEX REPLACE "[/_\\.]" "-" example_name ${src_rel_path})

    # Put hw-specific part of the name in front.
    # It is important for examples in subdirectories.
    foreach(pat "cpu-" "gpu-" "cross-engine-")
        string(REGEX REPLACE "^(.*)${pat}" "${pat}\\1"
            example_name ${example_name})
    endforeach()

    if(${example_name} MATCHES "(cross-engine|cpu|gpu)-")
        # Example name contains cross-engine, cpu or gpu
        if(NOT ${example_name} MATCHES ".*opencl" OR DNNL_GPU_RUNTIME STREQUAL "OCL")
            register_example(${example_name} ${src} "test" ${LIBM})
        endif()
    else()
        set(cpu_rt_pattern "(SEQ|OMP|TBB|SYCL|DPCPP)")
        set(gpu_rt_pattern "(OCL|SYCL|DPCPP)")
        if(${example_name} MATCHES "sycl.*")
            set(cpu_rt_pattern "(SYCL|DPCPP)")
            set(gpu_rt_pattern "(SYCL|DPCPP)")
        endif()
        if(DNNL_CPU_RUNTIME MATCHES ${cpu_rt_pattern})
            # Adding test for CPU
            add_test("cpu-${example_name}" "${example_name}" cpu)
            maybe_configure_test("cpu-${example_name}" TEST)
        endif()
        if(DNNL_GPU_RUNTIME MATCHES ${gpu_rt_pattern})
            # Adding test for GPU
            add_test("gpu-${example_name}" "${example_name}" gpu)
            maybe_configure_test("gpu-${example_name}" TEST)
        endif()
        register_example(${example_name} ${src} "" ${LIBM})
    endif()
endforeach()
