if (MSVC)
  set(ENOKI_NONE_FLAGS /DENOKI_DISABLE_VECTORIZATION)
  if (CMAKE_SIZEOF_VOID_P EQUAL 8)
    set(ENOKI_SSE42_FLAGS /D__SSE4_2__)
  else()
    set(ENOKI_SSE42_FLAGS /arch:SSE2 /D__SSE4_2__)
  endif()
  set(ENOKI_AVX_FLAGS /arch:AVX)
  set(ENOKI_AVX2_FLAGS /arch:AVX2)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
  add_compile_options(-wd11074 -wd11076)
  set(ENOKI_NONE_FLAGS -DENOKI_DISABLE_VECTORIZATION -ffp-contract=off)
  set(ENOKI_SSE42_FLAGS -xSSE4.2)
  set(ENOKI_AVX_FLAGS -xCORE-AVX-I)
  set(ENOKI_AVX2_FLAGS -xCORE-AVX2)
  set(ENOKI_AVX512_KNL_FLAGS -xMIC-AVX512)
  set(ENOKI_AVX512_SKX_FLAGS -xCORE-AVX512)
else()
  set(ENOKI_NONE_FLAGS -DENOKI_DISABLE_VECTORIZATION -ffp-contract=off)
  set(ENOKI_SSE42_FLAGS -msse4.2)
  set(ENOKI_AVX_FLAGS -mavx)
  set(ENOKI_AVX2_FLAGS -mavx2 -mfma -mf16c -mbmi -mbmi2 -mlzcnt)
  if (APPLE AND ${CMAKE_CXX_COMPILER_ID} STREQUAL GNU)
    set(ENOKI_AVX512_KNL_FLAGS -march=knl -Wa,-march=knl)
    set(ENOKI_AVX512_SKX_FLAGS -march=skylake-avx512 -Wa,-march=skx)
  else()
    set(ENOKI_AVX512_KNL_FLAGS -march=knl)
    set(ENOKI_AVX512_SKX_FLAGS -march=skylake-avx512)
  endif()
  set(ENOKI_NEON_FLAGS )
  if (${CMAKE_SYSTEM_PROCESSOR} MATCHES armv7)
    set(ENOKI_NEON_FLAGS -march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -mfp16-format=ieee)
  elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES aarch64)
    set(ENOKI_NEON_FLAGS -march=armv8-a+simd -mtune=cortex-a53)
  endif()
endif()

enoki_set_compile_flags()

if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR
    CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR
    CMAKE_CXX_COMPILER_ID MATCHES "Intel")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
  if (NOT ${ENOKI_U_CMAKE_BUILD_TYPE} MATCHES DEBUG)
    add_compile_options( -fvisibility=hidden)
  endif()
  add_compile_options(-Wall -Wextra -Wconversion)
elseif(WIN32)
  add_compile_options("/std:c++17")
  add_compile_options("/bigobj")
endif()

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

add_custom_target(check
        ${CMAKE_COMMAND} -E echo CWD=${CMAKE_BINARY_DIR}
        COMMAND ${CMAKE_COMMAND} -E echo CMD=${CMAKE_CTEST_COMMAND} -C $<CONFIG>
        COMMAND ${CMAKE_COMMAND} -E echo ----------------------------------
        COMMAND ${CMAKE_COMMAND} -E env CTEST_OUTPUT_ON_FAILURE=1
            ${CMAKE_CTEST_COMMAND} -C $<CONFIG>
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    DEPENDS ALL_BUILD USES_TERMINAL
)

function(enoki_test NAME)
  if (ENOKI_TEST_NAME)
    if (NOT "${ENOKI_TEST_NAME}" STREQUAL "${NAME}")
      return()
    endif()
  endif()
  add_executable(${NAME}_none ${ARGN} ${ENOKI_HEADERS})
  add_test(${NAME}_none_test ${NAME}_none)
  set_tests_properties(${NAME}_none_test PROPERTIES LABELS "none")
  set_target_properties(${NAME}_none PROPERTIES FOLDER ${NAME})
  target_compile_options(${NAME}_none PRIVATE ${ENOKI_NONE_FLAGS})

  if (ENOKI_HOST MATCHES "INTEL")
    add_executable(${NAME}_sse42 ${ARGN} ${ENOKI_HEADERS})
    target_compile_options(${NAME}_sse42 PRIVATE ${ENOKI_SSE42_FLAGS})
    set_target_properties(${NAME}_sse42 PROPERTIES FOLDER ${NAME})
    if (ENOKI_TEST_SSE42)
      add_test(${NAME}_sse42_test ${NAME}_sse42)
      set_tests_properties(${NAME}_sse42_test PROPERTIES LABELS "sse42")
    endif()

    add_executable(${NAME}_avx ${ARGN} ${ENOKI_HEADERS})
    target_compile_options(${NAME}_avx PRIVATE ${ENOKI_AVX_FLAGS})
    set_target_properties(${NAME}_avx PROPERTIES FOLDER ${NAME})
    if (ENOKI_TEST_AVX)
      add_test(${NAME}_avx_test ${NAME}_avx)
      set_tests_properties(${NAME}_avx_test PROPERTIES LABELS "avx")
    endif()

    add_executable(${NAME}_avx2 ${ARGN} ${ENOKI_HEADERS})
    target_compile_options(${NAME}_avx2 PRIVATE ${ENOKI_AVX2_FLAGS})
    set_target_properties(${NAME}_avx2 PROPERTIES FOLDER ${NAME})
    if (ENOKI_TEST_AVX2)
      add_test(${NAME}_avx2_test ${NAME}_avx2)
      set_tests_properties(${NAME}_avx2_test PROPERTIES LABELS "avx2")
    endif()

    if (NOT MSVC)
      add_executable(${NAME}_avx512_knl ${ARGN} ${ENOKI_HEADERS})
      target_compile_options(${NAME}_avx512_knl PRIVATE ${ENOKI_AVX512_KNL_FLAGS})
      set_target_properties(${NAME}_avx512_knl PROPERTIES FOLDER ${NAME})
      if (ENOKI_TEST_KNL)
        add_test(${NAME}_avx512_knl_test ${NAME}_avx512_knl)
        set_tests_properties(${NAME}_avx512_knl_test PROPERTIES LABELS "avx512_knl")
      endif()

      add_executable(${NAME}_avx512_skx ${ARGN} ${ENOKI_HEADERS})
      target_compile_options(${NAME}_avx512_skx PRIVATE ${ENOKI_AVX512_SKX_FLAGS})
      set_target_properties(${NAME}_avx512_skx PROPERTIES FOLDER ${NAME})

      if (ENOKI_TEST_SKX)
        add_test(${NAME}_avx512_skx_test ${NAME}_avx512_skx)
        set_tests_properties(${NAME}_avx512_skx_test PROPERTIES LABELS "avx512_skx")
      endif()
    endif()
  endif()

  if (ENOKI_HOST MATCHES "ARM")
    add_executable(${NAME}_neon ${ARGN} ${ENOKI_HEADERS})
    target_compile_options(${NAME}_neon PRIVATE ${ENOKI_NEON_FLAGS})
    set_target_properties(${NAME}_neon PROPERTIES FOLDER ${NAME})
    if (ENOKI_TEST_ARM)
      add_test(${NAME}_neon_test ${NAME}_neon)
      set_tests_properties(${NAME}_neon_test PROPERTIES LABELS "neon")
    endif()
  endif()
endfunction()

enoki_test(basic basic.cpp)
enoki_test(float float.cpp)
enoki_test(integer integer.cpp)
enoki_test(explog explog.cpp)
enoki_test(trig trig.cpp)
enoki_test(nested nested.cpp)
enoki_test(idiv idiv.cpp)
enoki_test(hyperbolic hyperbolic.cpp)
enoki_test(memory memory.cpp)
enoki_test(memory2 memory2.cpp)
enoki_test(horiz horiz.cpp)
enoki_test(vector vector.cpp)
enoki_test(conv conv.cpp)
enoki_test(dynamic dynamic.cpp)
enoki_test(histogram histogram.cpp)
enoki_test(sphere sphere.cpp)
enoki_test(complex complex.cpp)
enoki_test(morton morton.cpp)
enoki_test(special special.cpp)
enoki_test(call call.cpp)
enoki_test(sh sh.cpp)
enoki_test(color color.cpp)
enoki_test(custom custom.cpp)

if (ENOKI_AUTODIFF)
  enoki_set_native_flags()
  add_executable(autodiff_native autodiff.cpp)
  add_test(autodiff_native_test autodiff_native)
  set_tests_properties(autodiff_native_test PROPERTIES LABELS "native")
  set_target_properties(autodiff_native PROPERTIES FOLDER autodiff)
  target_link_libraries(autodiff_native PRIVATE enoki-autodiff)
  if (ENOKI_CUDA)
    target_link_libraries(autodiff_native PRIVATE enoki-cuda cuda)
  endif()
endif()
