# SPDX-License-Identifier: Apache-2.0

# cmake: Eclipse CDT4 generator amendment
#
#1. The generator handles just the COMPILE_DEFINITIONS.
#   (See: __ZEPHYR_SUPERVISOR__)
#   For the defines in INTERFACE_COMPILE_DEFINITIONS
#   a special handling is necessary.
#
#   Solution:
#   The amendment function generates a macro header file
#   ${CMAKE_BINARY_DIR}/zephyr/include/generated/cmake_intdef.h
#   based on INTERFACE_COMPILE_DEFINITIONS and appends the
#   defines from the file to
#   CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS.
#
#2. Eclipse CDT4 indexer has problems with CMake 'Eclipse CDT4 - x'
#   generator projects that use mixed C/C++.
#   The Eclipse CDT4 indexer is showing a lot of unresolved symbol
#   errors, when the project was created with CMake generator and
#   the code is a mix of C and C++.
#   The root cause of the problem is the g++ built-in __cplusplus macro,
#   that is passed by CMake generator in the '.cproject' file.
#   The defines in '.cproject' are always the same for C and C++.
#   In mixed C/C++ projects, the header files often contain the following
#   lines to let C++ code call the C functions:
#
#   #ifdef __cplusplus
#   extern "C" {
#   #endif
#
#   < header content >
#
#   #ifdef __cplusplus
#   }
#   #endif
#
#   Whenever the Eclipse CDT4 indexer compiles the code for
#   code analysis, it has the macro __cplusplus set,
#   independent of whether standard C or C++ source files are compiled.
#   The 'extern "C"' confuses the standard C compilation and the indexer
#   is messed up.
#
#   Solution:
#   The amendment function deletes the __cplusplus entry from
#   CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS.
#
#3. The amendment function appends the defines from
#   ${CMAKE_BINARY_DIR}/zephyr/include/generated/autoconf.h to
#   CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS.
#
function(eclipse_cdt4_generator_amendment _param_defs)
#
# _param_defs handled values:
#
#   1 - work mode: "C and CXX includes, defines in .cproject without __cplusplus"
#   2 - work mode: "C and CXX includes, defines in .cproject with __cplusplus"
#   5 - work mode: "C and CXX includes, defines in Eclipse with project defines"
#
# In any other case,
# the C and CXX includes, defines setting in Eclipse is the user task.
#
##   EXPERIMENTAL:
##   3 - work mode: "C and CXX includes, defines in .settings - [EXPERIMENTAL]"
##   4 - work mode: "C and CXX includes, defines in .settings with project defines - [EXPERIMENTAL]"
#
message("   Eclipse CDT4 generator amendment mode:")
if (${_param_defs} EQUAL 1)
  set(_work_mode "C and CXX includes, defines in .cproject without __cplusplus")
  message("   ${_work_mode}")
  message("   with project includes and defines")
elseif (${_param_defs} EQUAL 2)
  set(_work_mode "C and CXX includes, defines in .cproject with __cplusplus")
  message("   ${_work_mode}")
  message("   with project includes and defines")
#elseif (${_param_defs} EQUAL 3)
#  set(_work_mode "C and CXX includes, defines in .settings - [EXPERIMENTAL]")
#  message("   ${_work_mode}")
#  message("   without project defines")
#elseif (${_param_defs} EQUAL 4)
#  set(_work_mode "C and CXX includes, defines in .settings with project defines - [EXPERIMENTAL]")
#  message("   ${_work_mode}")
elseif (${_param_defs} EQUAL 5)
  set(_work_mode "C and CXX includes, defines in Eclipse with project defines")
  message("   ${_work_mode}")
else(${_param_defs} EQUAL 1)
  set(_work_mode "C and CXX includes, defines setting in Eclipse is the user task")
  message("   ${_work_mode}")
endif(${_param_defs} EQUAL 1)

set(OUTPUT_FILE ${CMAKE_BINARY_DIR}/zephyr/include/generated/cmake_intdef.h)
file(WRITE ${OUTPUT_FILE}  "/* Generated by eclipse_cd4_generator_amendment.cmake */\n")
file(APPEND ${OUTPUT_FILE} "/* The header contains the defines collected from the */\n")
file(APPEND ${OUTPUT_FILE} "/*   INTERFACE_COMPILE_DEFINITIONS target property    */\n")
file(APPEND ${OUTPUT_FILE} "/*        corresponding to ${IMAGE}zephyr_interface   */\n")
get_target_property(_int_comp_def ${IMAGE}zephyr_interface INTERFACE_COMPILE_DEFINITIONS)
foreach( d ${_int_comp_def} )
  string(REGEX MATCH "([A-Za-z_][A-Za-z0-9_]*) *=* *(.*) *$" _dummy "${d}")
  file(APPEND ${OUTPUT_FILE} "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}\n")
endforeach()

if (${_work_mode} STREQUAL "C and CXX includes, defines in Eclipse with project defines")
  message("")
  message("   -------------------------------------------------------------------------")
  message("   Add the following two command line parameters:")
  message("")
  message("   -imacros ${CMAKE_BINARY_DIR}/zephyr/include/generated/cmake_intdef.h")
  message("   -imacros ${AUTOCONF_H}")
  message("")
  message("   to 'CDT cross GCC Built-in Compiler Settings' provider command definition")
  message("   -------------------------------------------------------------------------")
  message("")
endif()

if ( (${_work_mode} STREQUAL "C and CXX includes, defines in .settings - [EXPERIMENTAL]") OR
     (${_work_mode} STREQUAL "C and CXX includes, defines in .settings with project defines - [EXPERIMENTAL]") )

  set(OUTPUT_FILE ${CMAKE_BINARY_DIR}/.settings/language.settings.xml)
  file(WRITE ${OUTPUT_FILE} "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n")

  file(APPEND ${OUTPUT_FILE} "<project>\n")
  file(APPEND ${OUTPUT_FILE} "\t<configuration id=\"org.eclipse.cdt.core.default.config.1\" name=\"Configuration\">\n")
  file(APPEND ${OUTPUT_FILE} "\t\t<extension point=\"org.eclipse.cdt.core.LanguageSettingsProvider\">\n")
  file(APPEND ${OUTPUT_FILE} "\t\t\t<provider copy-of=\"extension\" id=\"org.eclipse.cdt.ui.UserLanguageSettingsProvider\"/>\n")
  file(APPEND ${OUTPUT_FILE} "\t\t\t<provider-reference id=\"org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider\" ref=\"shared-provider\"/>\n")
  file(APPEND ${OUTPUT_FILE} "\t\t\t<provider-reference id=\"org.eclipse.cdt.core.PathEntryScannerInfoLanguageSettingsProvider\" ref=\"shared-provider\"/>\n")
  if (${_work_mode} STREQUAL "C and CXX includes, defines in .settings with project defines - [EXPERIMENTAL]")
    file(APPEND ${OUTPUT_FILE} "\t\t\t<provider class=\"org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector\" console=\"false\" id=\"org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector\" keep-relative-paths=\"false\" name=\"Zephyr autoconf and SDK Compiler Settings\" parameter=\"${CROSS_COMPILE}\${COMMAND} \${FLAGS} -imacros ${CMAKE_BINARY_DIR}/zephyr/include/generated/cmake_intdef.h -imacros ${AUTOCONF_H} -E -P -v -dD &quot;\${INPUTS}&quot;\" store-entries-with-project=\"true\">\n")
  else ()
    file(APPEND ${OUTPUT_FILE} "\t\t\t<provider class=\"org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector\" console=\"false\" id=\"org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector\" keep-relative-paths=\"false\" name=\"Zephyr autoconf and SDK Compiler Settings\" parameter=\"${CROSS_COMPILE}\${COMMAND} \${FLAGS} -E -P -v -dD &quot;\${INPUTS}&quot;\" store-entries-with-project=\"true\">\n")
  endif ()
  file(APPEND ${OUTPUT_FILE} "\t\t\t\t<language-scope id=\"org.eclipse.cdt.core.gcc\"/>\n")
  file(APPEND ${OUTPUT_FILE} "\t\t\t\t<language-scope id=\"org.eclipse.cdt.core.g++\"/>\n")
  file(APPEND ${OUTPUT_FILE} "\t\t\t</provider>\n")
  file(APPEND ${OUTPUT_FILE} "\t\t</extension>\n")
  file(APPEND ${OUTPUT_FILE} "\t</configuration>\n")
  file(APPEND ${OUTPUT_FILE} "</project>\n")

endif ()

if ( (${_work_mode} STREQUAL "C and CXX includes, defines in .cproject without __cplusplus") OR
     (${_work_mode} STREQUAL "C and CXX includes, defines in .cproject with __cplusplus") OR
     (${_work_mode} STREQUAL "C and CXX includes, defines in .settings - [EXPERIMENTAL]") )

  if ( (${_work_mode} STREQUAL "C and CXX includes, defines in .cproject without __cplusplus") OR
       (${_work_mode} STREQUAL "C and CXX includes, defines in .cproject with __cplusplus") )

    if (${_work_mode} STREQUAL "C and CXX includes, defines in .cproject without __cplusplus")
      # Delete __cplusplus define
      set(TEMP_VAR "${CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS}")
      STRING(REGEX REPLACE "__cplusplus;.*;" "" TEMP_VAR "${TEMP_VAR}")
      set(CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS "${TEMP_VAR}" CACHE INTERNAL "CXX compiler system defined macros")
    endif()

  else()

    # Wiping C compiler system defined macros and include directories
    if (CMAKE_C_COMPILER_ID MATCHES GNU)
      set(CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS "" CACHE INTERNAL "C compiler system include directories")
      set(CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS "" CACHE INTERNAL "C compiler system defined macros")
    endif ()
    # Wiping CXX compiler system defined macros and include directories
    if ("${CMAKE_CXX_COMPILER_ID}" MATCHES GNU)
      set(CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS "" CACHE INTERNAL "CXX compiler system include directories")
      set(CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS "" CACHE INTERNAL "CXX compiler system defined macros")
    endif ()

  endif()

  file(STRINGS ${CMAKE_BINARY_DIR}/zephyr/include/generated/cmake_intdef.h _int_comp_def)
  set (_resultDefines "${CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS}")
  foreach( d ${_int_comp_def} )
    string(REGEX MATCH "^#define +([A-Za-z_][A-Za-z0-9_]*) *(.*) *$" _dummy "${d}")
    if (NOT ("${CMAKE_MATCH_1}" STREQUAL "") )
      list(APPEND _resultDefines "${CMAKE_MATCH_1}")
      if ("${CMAKE_MATCH_2}" STREQUAL "")
        list(APPEND _resultDefines "")
      else()
        list(APPEND _resultDefines "${CMAKE_MATCH_2}")
      endif()
    endif()
  endforeach()
  set (CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS "${_resultDefines}" CACHE INTERNAL "C compiler system defined macros")

  file(STRINGS ${AUTOCONF_H} _auto_conf)
  set (_resultDefines "${CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS}")
  foreach( d ${_auto_conf} )
    string(REGEX MATCH "^#define +([A-Za-z_][A-Za-z0-9_]*) *(.*) *$" _dummy "${d}")
    if (NOT ("${CMAKE_MATCH_1}" STREQUAL "") )
      list(APPEND _resultDefines "${CMAKE_MATCH_1}")
      if ("${CMAKE_MATCH_2}" STREQUAL "")
        list(APPEND _resultDefines " ")
      else()
        list(APPEND _resultDefines "${CMAKE_MATCH_2}")
      endif()
    endif()
  endforeach()
  set (CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS "${_resultDefines}" CACHE INTERNAL "C compiler system defined macros")

endif()

endfunction(eclipse_cdt4_generator_amendment _param_defs)
