#-----------------------------*-cmake-*----------------------------------------#
# file   windows-cl.cmake
# author Kelly Thompson <kgt@lanl.gov>
# date   2010 June 5
# brief  Establish flags for Windows - MSVC
# note   Copyright (C) 2016-2019 Triad National Security, LLC.
#        All rights reserved.
#------------------------------------------------------------------------------#

include_guard(GLOBAL)

# Useful reference information:
# https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=vs-2017

#
# Sanity Checks
#
if( NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio" AND
    NOT ${CMAKE_GENERATOR} MATCHES  "NMake Makefiles" )
  message( FATAL_ERROR
  "config/windows-cl.cmake must be taught to build for this compiler "
  "(CMAKE_GENERATOR = ${CMAKE_GENERATOR}). Yell at kt for help on this error." )
endif()

#
# Compiler flag checks
#

# This is required to provide compatibility between MSVC and MinGW generated
# libraries.
if( DRACO_SHARED_LIBS )
  set( CMAKE_GNUtoMS ON CACHE BOOL "Compatibility flag for MinGW/MSVC." FORCE)
endif()

# Extra setup (ds++/config.h) for MSVC
# 1. Allow M_PI to be found via <cmath>
set( _USE_MATH_DEFINES 1 )

# Note that in component_macros.cmake, build targets are assigned the property
# WINDOWS_EXPORT_ALL_SYMBOLS=TRUE.  See Ref:
# https://blog.kitware.com/create-dlls-on-windows-without-declspec-using-new-cmake-export-all-feature/
# for details. However, we still use DLL_PUBLIC macros to control public
# visibility of global variables. See dll_declspec.h generated by ds++'s
# CMakeLists.txt.

set( MD_or_MT_debug "${MD_or_MT}d" )
if( DEFINED DEBUG_RUNTIME_EXT AND "${DEBUG_RUNTIME_EXT}" STREQUAL "d" )
  set( MD_or_MT_debug "${MD_or_MT}${DEBUG_RUNTIME_EXT} /RTC1" )
endif()

set( numproc $ENV{NUMBER_OF_PROCESSORS} )
if( "${numproc}notfound" STREQUAL "notfound" )
  set( numproc 1 )
endif()

if( NOT CXX_FLAGS_INITIALIZED )
  set( CXX_FLAGS_INITIALIZED "yes" CACHE INTERNAL "using draco settings." )

  # Alternative for per-directory, or per-target specific flags:
  # add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/MP>")

  # Notes on options:
  # - /wd 4251 disable warning #4251: 'identifier' : class 'type' needs to have
  #   dll-interface to be used by clients of class 'type2'
  # - /arch:[SSE|SSE2|AVX|AVX2|IA32]
  # - /W[1234] Warning levels. Draco is currently using /W4 (see 
  #            src/CMakeLists.txt), but clients default to /W2.
  # - /std:c++14 (should be added by cmake in compilerEnv.cmake)
  # - /showIncludes
  # - /FC
  set( CMAKE_C_FLAGS "/W2 /Gy /fp:precise /DWIN32 /D_WINDOWS /MP /wd4251" )
  if(HAVE_HARDWARE_AVX2)
    string(APPEND CMAKE_C_FLAGS " /arch:AVX2")
  endif()
  set( CMAKE_C_FLAGS_DEBUG "/${MD_or_MT_debug} /Od /Zi /DDEBUG /D_DEBUG" )
  set( CMAKE_C_FLAGS_RELEASE "/${MD_or_MT} /O2 /DNDEBUG" )
  set( CMAKE_C_FLAGS_MINSIZEREL "/${MD_or_MT} /O1 /DNDEBUG" )
  set( CMAKE_C_FLAGS_RELWITHDEBINFO "/${MD_or_MT} /O2 /Zi /DDEBUG" )

  # Suppress some MSVC warnings about "unsafe" pointer use.
  if(MSVC_VERSION GREATER 1399)
    string( APPEND CMAKE_C_FLAGS
      " /D_CRT_SECURE_NO_DEPRECATE /D_SCL_SECURE_NO_DEPRECATE /D_SECURE_SCL=0" )
    #string( APPEND CMAKE_C_FLAGS_DEBUG
    #        "${CMAKE_C_FLAGS_DEBUG} /D_HAS_ITERATOR_DEBUGGING=0" )
  endif()

  # If building static libraries, inlcude debugging information in the library.
  if( ${DRACO_LIBRARY_TYPE} MATCHES "STATIC" )
    string( APPEND CMAKE_C_FLAGS_DEBUG " /Z7"   )
  endif()

  set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHa" )
  set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}" )
  set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" )
  set( CMAKE_CXX_FLAGS_MINSIZEREL "/${MD_or_MT} /O1 /DNDEBUG" )
  set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "/${MD_or_MT} /O2 /Zi /DDEBUG" )

endif()

##---------------------------------------------------------------------------##
# Ensure cache values always match current selection
##---------------------------------------------------------------------------##
set( CMAKE_C_FLAGS                "${CMAKE_C_FLAGS}"                CACHE STRING "compiler flags" FORCE )
set( CMAKE_C_FLAGS_DEBUG          "${CMAKE_C_FLAGS_DEBUG}"          CACHE STRING "compiler flags" FORCE )
set( CMAKE_C_FLAGS_RELEASE        "${CMAKE_C_FLAGS_RELEASE}"        CACHE STRING "compiler flags" FORCE )
set( CMAKE_C_FLAGS_MINSIZEREL     "${CMAKE_C_FLAGS_MINSIZEREL}"     CACHE STRING "compiler flags" FORCE )
set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}" CACHE STRING "compiler flags" FORCE )

set( CMAKE_CXX_FLAGS                "${CMAKE_CXX_FLAGS}"                CACHE STRING "compiler flags" FORCE )
set( CMAKE_CXX_FLAGS_DEBUG          "${CMAKE_CXX_FLAGS_DEBUG}"          CACHE STRING "compiler flags" FORCE )
set( CMAKE_CXX_FLAGS_RELEASE        "${CMAKE_CXX_FLAGS_RELEASE}"        CACHE STRING "compiler flags" FORCE )
set( CMAKE_CXX_FLAGS_MINSIZEREL     "${CMAKE_CXX_FLAGS_MINSIZEREL}"     CACHE STRING "compiler flags" FORCE )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "compiler flags" FORCE )

#
# Toggle compiler flags for optional features
#
if( NOT "${OpenMP_C_FLAGS}x" STREQUAL "x" )
  toggle_compiler_flag( OPENMP_FOUND ${OpenMP_C_FLAGS} "C;CXX" "" )
endif()

#
# Extra runtime libraries...
#

# Locate a Windows sockets library (required!)
if( CMAKE_CL_64 )
  set( winsock_suffix_dir "x64")
else()
  set( winsock_suffix_dir "x86")
endif()
foreach( lib ws2_32;wsock32;winsock32;mswsock32 )
  if( NOT Lib_win_winsock )
    find_library( winsock_lib_${lib} ${lib} )
    find_library( winsock_lib_${lib}
      NAMES ${lib}
      HINTS "C:/Windows/System32"
            "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.17134.0/um/${winsock_suffix_dir}" )
    set( Lib_win_winsock "${winsock_lib_${lib}}" CACHE FILEPATH
      "Windows sockets library.")
  endif()
endforeach()

# winsock is a required dependency.
if( NOT Lib_win_winsock )
  message( FATAL_ERROR "Could not find library wsock32, mswsock32 or ws2_32!" )
endif()

# Extra logic to ensure correct winsock is found.
if( "${Lib_win_winsock}" MATCHES "um/x86" AND CMAKE_CL_64 )
  message( FATAL_ERROR "Found 32-bit winsock (${Lib_win_winsock} but targeting x64 architecture. Ensure that cmake is run from the x64 Visual Studio Command Prompt." )
elseif( "${Lib_win_winsock}" MATCHES "um/x64" AND NOT CMAKE_CL_64 )
  message( FATAL_ERROR "Found 64-bit winsock (${Lib_win_winsock} but targeting x86 architecture. Ensure that cmake is run from the x86 Visual Studio Command Prompt." )
endif()

#------------------------------------------------------------------------------#
# End windows-cl.cmake
#------------------------------------------------------------------------------#
