# Copyright 2020 The Shaderc Authors. All rights reserved.
#
# 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.

# utility functions

function (shaderc_use_gmock TARGET)
  target_include_directories(${TARGET} PRIVATE
    ${gmock_SOURCE_DIR}/include
    ${gtest_SOURCE_DIR}/include)
  target_link_libraries(${TARGET} PRIVATE gmock gtest_main)
endfunction(shaderc_use_gmock)

function(shaderc_default_c_compile_options TARGET)
  if (NOT "${MSVC}")
    if (SHADERC_ENABLE_WERROR_COMPILE)
      target_compile_options(${TARGET} PRIVATE -Wall -Werror -fvisibility=hidden)
    else()
      target_compile_options(${TARGET} PRIVATE -Wall -fvisibility=hidden)
    endif()
    check_cxx_compiler_flag(-fPIC COMPILER_SUPPORTS_PIC)
    if (NOT "${MINGW}" AND COMPILER_SUPPORTS_PIC)
      target_compile_options(${TARGET} PRIVATE -fPIC)
    endif()
    if (ENABLE_CODE_COVERAGE)
      # The --coverage option is a synonym for -fprofile-arcs -ftest-coverage
      # when compiling.
      target_compile_options(${TARGET} PRIVATE -g -O0 --coverage)
      # The --coverage option is a synonym for -lgcov when linking for gcc.
      # For clang, it links in a different library, libclang_rt.profile, which
      # requires clang to be built with compiler-rt.
      target_link_libraries(${TARGET} PRIVATE --coverage)
    endif()
    if (NOT SHADERC_ENABLE_SHARED_CRT)
      if (WIN32)
        # For MinGW cross compile, statically link to the libgcc runtime.
        # But it still depends on MSVCRT.dll.
        set_target_properties(${TARGET} PROPERTIES
          LINK_FLAGS "-static -static-libgcc")
      endif(WIN32)
    endif(NOT SHADERC_ENABLE_SHARED_CRT)
  else()
    # disable warning C4800: 'int' : forcing value to bool 'true' or 'false'
    # (performance warning)
    target_compile_options(${TARGET} PRIVATE /wd4800)
  endif()
endfunction(shaderc_default_c_compile_options)

function(shaderc_default_compile_options TARGET)
  shaderc_default_c_compile_options(${TARGET})
  if (NOT "${MSVC}")
    target_compile_options(${TARGET} PRIVATE -std=c++11)
    if (NOT SHADERC_ENABLE_SHARED_CRT)
      if (WIN32)
        # For MinGW cross compile, statically link to the C++ runtime.
        # But it still depends on MSVCRT.dll.
        set_target_properties(${TARGET} PROPERTIES
          LINK_FLAGS "-static -static-libgcc -static-libstdc++")
      endif(WIN32)
    endif(NOT SHADERC_ENABLE_SHARED_CRT)
  endif()
endfunction(shaderc_default_compile_options)

# Build an asciidoc file; additional arguments past the base filename specify
# additional dependencies for the file.
function(shaderc_add_asciidoc TARGET FILE)
  if (ASCIIDOCTOR_EXE)
    set(DEST ${CMAKE_CURRENT_BINARY_DIR}/${FILE}.html)
    add_custom_command(
      COMMAND ${ASCIIDOCTOR_EXE} -a toc -o ${DEST}
        ${CMAKE_CURRENT_SOURCE_DIR}/${FILE}.asciidoc
      DEPENDS ${FILE}.asciidoc ${ARGN}
      OUTPUT ${DEST})
    # Create the target, but the default build target does not depend on it.
    # Some Asciidoctor installations are mysteriously broken, and it's hard
    # to detect those cases.  Generating HTML is not critical by default.
    add_custom_target(${TARGET} DEPENDS ${DEST})
  endif(ASCIIDOCTOR_EXE)
endfunction()

# Adds a set of tests.
# This function accepts the following parameters:
# TEST_PREFIX:  a prefix for each test target name
# TEST_NAMES:   a list of test names where each TEST_NAME has a corresponding
#               file residing at src/${TEST_NAME}_test.cc
# LINK_LIBS:    (optional) a list of libraries to be linked to the test target
# INCLUDE_DIRS: (optional) a list of include directories to be searched
#               for header files.
function(shaderc_add_tests)
  if(${SHADERC_ENABLE_TESTS})
    cmake_parse_arguments(PARSED_ARGS
      ""
      "TEST_PREFIX"
      "TEST_NAMES;LINK_LIBS;INCLUDE_DIRS"
      ${ARGN})
    if (NOT PARSED_ARGS_TEST_NAMES)
      message(FATAL_ERROR "Tests must have a target")
    endif()
    if (NOT PARSED_ARGS_TEST_PREFIX)
      message(FATAL_ERROR "Tests must have a prefix")
    endif()
    foreach(TARGET ${PARSED_ARGS_TEST_NAMES})
      set(TEST_NAME ${PARSED_ARGS_TEST_PREFIX}_${TARGET}_test)
      add_executable(${TEST_NAME} src/${TARGET}_test.cc)
      shaderc_default_compile_options(${TEST_NAME})
      if (MINGW)
        target_compile_options(${TEST_NAME} PRIVATE -DSHADERC_DISABLE_THREADED_TESTS)
      endif()
      if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
        # Disable this warning, which is useless in test code.
        # Fixes https://github.com/google/shaderc/issues/334
        target_compile_options(${TEST_NAME} PRIVATE -Wno-noexcept-type)
      endif()
      if (PARSED_ARGS_LINK_LIBS)
        target_link_libraries(${TEST_NAME} PRIVATE
        ${PARSED_ARGS_LINK_LIBS})
      endif()
      if (PARSED_ARGS_INCLUDE_DIRS)
        target_include_directories(${TEST_NAME} PRIVATE
        ${PARSED_ARGS_INCLUDE_DIRS})
      endif()
      shaderc_use_gmock(${TEST_NAME})
      add_test(
        NAME ${PARSED_ARGS_TEST_PREFIX}_${TARGET}
        COMMAND ${TEST_NAME})
    endforeach()
  endif(${SHADERC_ENABLE_TESTS})
endfunction(shaderc_add_tests)

# Finds all transitive static library dependencies of a given target
# including possibly the target itself.
# This will skip libraries that were statically linked that were not
# built by CMake, for example -lpthread.
macro(shaderc_get_transitive_libs target out_list)
  if (TARGET ${target})
    get_target_property(libtype ${target} TYPE)
    # If this target is a static library, get anything it depends on.
    if ("${libtype}" STREQUAL "STATIC_LIBRARY")
      # Get the original library if this is an alias library. This is
      # to avoid putting both the original library and the alias library
      # in the list (given we are deduplicating according to target names).
      # Otherwise, we may pack the same library twice, resulting in
      # duplicated symbols.
      get_target_property(aliased_target ${target} ALIASED_TARGET)
      if (aliased_target)
        list(INSERT ${out_list} 0 "${aliased_target}")
      else()
        list(INSERT ${out_list} 0 "${target}")
      endif()

      get_target_property(libs ${target} LINK_LIBRARIES)
      if (libs)
        foreach(lib ${libs})
          shaderc_get_transitive_libs(${lib} ${out_list})
        endforeach()
      endif()
    endif()
  endif()
  # If we know the location (i.e. if it was made with CMake) then we
  # can add it to our list.
  LIST(REMOVE_DUPLICATES ${out_list})
endmacro()

# Combines the static library "target" with all of its transitive static
# library dependencies into a single static library "new_target".
function(shaderc_combine_static_lib new_target target)

  set(all_libs "")
  shaderc_get_transitive_libs(${target} all_libs)

  set(libname
      ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${new_target}${CMAKE_STATIC_LIBRARY_SUFFIX})

  if (MSVC)
    string(REPLACE ";" ">;$<TARGET_FILE:" temp_string "${all_libs}")
    set(lib_target_list "$<TARGET_FILE:${temp_string}>")

    add_custom_command(OUTPUT ${libname}
      DEPENDS ${all_libs}
      COMMAND lib.exe ${lib_target_list} /OUT:${libname} /NOLOGO)
  elseif(APPLE)
    string(REPLACE ";" ">;$<TARGET_FILE:" temp_string "${all_libs}")
    set(lib_target_list "$<TARGET_FILE:${temp_string}>")

    add_custom_command(OUTPUT ${libname}
      DEPENDS ${all_libs}
      COMMAND libtool -static -o ${libname} ${lib_target_list})
  else()
    string(REPLACE ";" "> \naddlib $<TARGET_FILE:" temp_string "${all_libs}")
    set(start_of_file
      "create ${libname}\naddlib $<TARGET_FILE:${temp_string}>")
    set(build_script_file "${start_of_file}\nsave\nend\n")

    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${new_target}.ar"
        CONTENT ${build_script_file}
        CONDITION 1)

    add_custom_command(OUTPUT  ${libname}
      DEPENDS ${all_libs}
      COMMAND ${CMAKE_AR} -M < ${new_target}.ar)
  endif()

  add_custom_target(${new_target}_genfile ALL
    DEPENDS ${libname})

  # CMake needs to be able to see this as another normal library,
  # so import the newly created library as an imported library,
  # and set up the dependencies on the custom target.
  add_library(${new_target} STATIC IMPORTED)
  set_target_properties(${new_target}
    PROPERTIES IMPORTED_LOCATION ${libname})
  add_dependencies(${new_target} ${new_target}_genfile)
endfunction()
