#
# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.  Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#

################################################################################
# This file contains functionality related to compiling a single native source
# file (C, C++ or Objective-C) into an object file. It also harbours related
# functionality for generating PCH (precompiled headers) and Windows resource
# files.

################################################################################
# Creates a recipe that creates a compile_commands.json fragment. Remove any
# occurrences of FIXPATH programs from the command to show the actual invocation.
#
# Param 1: Name of file to create
# Param 2: Working directory
# Param 3: Source file
# Param 4: Compile command
################################################################################
define WriteCompileCommandsFragment
  $(call LogInfo, Creating compile commands fragment for $(notdir $3))
  $(call MakeDir, $(dir $1))
  $(call WriteFile, { \
      "directory": "$(strip $(call FixPath, $2))"$(COMMA) \
      "file": "$(strip $(call FixPath, $3))"$(COMMA) \
      "command": "$(strip $(subst $(DQUOTE),\$(DQUOTE),$(subst \,\\, \
        $(subst $(FIXPATH),,$(call FixPath, $4)))))" \
    }$(COMMA), \
    $1)
endef

################################################################################
# Extensions of files handled by this macro.
NATIVE_SOURCE_EXTENSIONS := %.S %.c %.cpp %.cc %.m %.mm

# Replaces native source extensions with the object file extension in a string.
# Param 1: the string containing source file names with extensions
# The surrounding strip is needed to keep additional whitespace out
define replace_with_obj_extension
$(strip \
  $(foreach extension, $(NATIVE_SOURCE_EXTENSIONS), \
      $(patsubst $(extension),%$(OBJ_SUFFIX), $(filter $(extension), $1))) \
)
endef

################################################################################
# This pattern is used to transform the output of the microsoft CL compiler
# into a make syntax dependency file (.d)
WINDOWS_SHOWINCLUDE_SED_PATTERN := \
    -e '/^Note: including file:/!d' \
    -e 's|Note: including file: *||' \
    -e 's|\r||g' \
    -e 's|\\|/|g' \
    -e 's|^\([a-zA-Z]\):|$(WINENV_PREFIX)/\1|g' \
    -e '\|$(TOPDIR)|I !d' \
    -e 's|$$$$| \\|g' \
    #

################################################################################
# This pattern is used to transform a dependency file (.d) to a list
# of make targets for dependent files (.d.targets)
DEPENDENCY_TARGET_SED_PATTERN := \
    -e 's/\#.*//' \
    -e 's/^[^:]*: *//' \
    -e 's/ *\\$$$$//' \
    -e 's/^[	 ]*//' \
    -e '/^$$$$/ d' \
    -e 's/$$$$/ :/' \
    #

################################################################################
# Create the recipe needed to compile a single native source file.
#
# Parameter 1 is the name of the rule, based on the name of the library/
# program being build and the name of the source code file, e.g.
# BUILD_LIBFOO_fooMain.cpp.
#
# Remaining parameters are named arguments:
#   FILE - The full path of the source file to compiler
#   BASE - The name of the rule for the entire binary to build ($1)
#
CreateCompiledNativeFile = $(NamedParamsMacroTemplate)
define CreateCompiledNativeFileBody
  $1_FILENAME := $$(notdir $$($1_FILE))

  # The target file to be generated.
  $1_OBJ := $$($$($1_BASE)_OBJECT_DIR)/$$(call replace_with_obj_extension, \
      $$($1_FILENAME))

  # Generate the corresponding compile_commands.json fragment.
  $1_OBJ_JSON = $$(MAKESUPPORT_OUTPUTDIR)/compile-commands/$$(subst /,_,$$(subst \
      $$(OUTPUTDIR)/,,$$($1_OBJ))).json
  $$($1_BASE)_ALL_OBJS_JSON += $$($1_OBJ_JSON)

  # Only continue if this object file hasn't been processed already. This lets
  # the first found source file override any other with the same name.
  ifeq ($$($1_OBJ_PROCESSED), )
    $1_OBJ_PROCESSED := true
    # This is the definite source file to use for $1_FILENAME.
    $1_SRC_FILE := $$($1_FILE)

    $$(eval $$(call SetupCompileFileFlags,$1,$$($1_BASE)))

    ifneq ($$(filter %.c, $$($1_FILENAME)), )
      # Compile as a C file
      $1_CFLAGS += $$($1_WARNINGS_FLAGS)
      $1_FLAGS := $(CFLAGS_CCACHE) $$($1_USE_PCH_FLAGS) $$($1_BASE_CFLAGS) \
          $$($1_OPT_CFLAGS) $$($1_CFLAGS) -c
      $1_COMPILER := $$($$($1_BASE)_CC)
    else ifneq ($$(filter %.m, $$($1_FILENAME)), )
      # Compile as an Objective-C file
      $1_CFLAGS += $$($1_WARNINGS_FLAGS)
      $1_FLAGS := -x objective-c $(CFLAGS_CCACHE) $$($1_USE_PCH_FLAGS) \
          $$($1_BASE_CFLAGS) $$($1_OPT_CFLAGS) $$($1_CFLAGS) -c
      $1_COMPILER := $$($$($1_BASE)_CC)
    else ifneq ($$(filter %.S, $$($1_FILENAME)), )
      # Compile as preprocessed assembler file
      $1_FLAGS := $(BASIC_ASFLAGS) $$($1_BASE_ASFLAGS)
      $1_COMPILER := $(AS)

      # gcc or clang assembly files must contain an appropriate relative .file
      # path for reproducible builds.
      ifneq ($(findstring $(TOOLCHAIN_TYPE), gcc clang), )
        # If no absolute paths allowed, work out relative source file path
        # for assembly .file substitution, otherwise use full file path
        ifeq ($(ALLOW_ABSOLUTE_PATHS_IN_OUTPUT), false)
          $1_REL_ASM_SRC := $$(call RelativePath, $$($1_FILE), $(WORKSPACE_ROOT))
        else
          $1_REL_ASM_SRC := $$($1_FILE)
        endif
        $1_FLAGS := $$($1_FLAGS) -DASSEMBLY_SRC_FILE='"$$($1_REL_ASM_SRC)"' \
            -include $(TOPDIR)/make/data/autoheaders/assemblyprefix.h
      endif
    else ifneq ($$(filter %.cpp %.cc %.mm, $$($1_FILENAME)), )
      # Compile as a C++ or Objective-C++ file
      $1_CXXFLAGS += $$($1_WARNINGS_FLAGS)
      $1_FLAGS := $(CFLAGS_CCACHE) $$($1_USE_PCH_FLAGS) $$($1_BASE_CXXFLAGS) \
          $$($1_OPT_CXXFLAGS) $$($1_CXXFLAGS) -c
      $1_COMPILER := $$($$($1_BASE)_CXX)
    else
      $$(error Internal error in NativeCompilation.gmk: no compiler for file $$($1_FILENAME))
    endif

    # And this is the dependency file for this obj file.
    $1_DEPS_FILE := $$(patsubst %$(OBJ_SUFFIX),%.d,$$($1_OBJ))
    # The dependency target file lists all dependencies as empty targets to
    # avoid make error "No rule to make target" for removed files
    $1_DEPS_TARGETS_FILE := $$(patsubst %$(OBJ_SUFFIX),%.d.targets,$$($1_OBJ))

    # Only try to load individual dependency information files if the global
    # file hasn't been loaded (could happen if make was interrupted).
    ifneq ($$($$($1_BASE)_DEPS_FILE_LOADED), true)
      # Include previously generated dependency information. (if it exists)
      -include $$($1_DEPS_FILE)
      -include $$($1_DEPS_TARGETS_FILE)
    endif

    ifneq ($$(strip $$($1_CFLAGS) $$($1_CXXFLAGS) $$($1_OPTIMIZATION)), )
      $1_VARDEPS := $$($1_CFLAGS) $$($1_CXXFLAGS) $$($1_OPTIMIZATION)
      $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, $$($1_OBJ).vardeps)
    endif

    $1_OBJ_DEPS := $$($1_SRC_FILE) $$($$($1_BASE)_COMPILE_VARDEPS_FILE) \
        $$($$($1_BASE)_EXTRA_DEPS) $$($1_VARDEPS_FILE)
    $1_COMPILE_OPTIONS := $$($1_FLAGS) $(CC_OUT_OPTION)$$($1_OBJ) $$($1_SRC_FILE)
    # For reproducible builds with gcc and clang ensure random symbol generation is
    # seeded deterministically
    ifneq ($(findstring $(TOOLCHAIN_TYPE), gcc clang), )
      $1_COMPILE_OPTIONS += -frandom-seed="$$($1_FILENAME)"
    endif

    $$($1_OBJ_JSON): $$($1_OBJ_DEPS)
	$$(call WriteCompileCommandsFragment, $$@, $$(PWD), $$($1_SRC_FILE), \
	    $$($1_COMPILER) $$($1_COMPILE_OPTIONS))

    $$($1_OBJ): $$($1_OBJ_DEPS) | $$($$($1_BASE)_BUILD_INFO)
	$$(call LogInfo, Compiling $$($1_FILENAME) (for $$($$($1_BASE)_BASENAME)))
	$$(call MakeDir, $$(@D))
        ifneq ($(TOOLCHAIN_TYPE), microsoft)
	  $$(call ExecuteWithLog, $$@, $$(call MakeCommandRelative, \
	      $$($1_COMPILER) $$(GENDEPS_FLAGS) \
	      $$(addsuffix .tmp, $$($1_DEPS_FILE)) \
	      $$($1_COMPILE_OPTIONS)))
          ifneq ($$($1_DEPS_FILE), )
	    $$(call fix-deps-file, $$($1_DEPS_FILE))
            # Create a dependency target file from the dependency file.
            # Solution suggested by:
            # http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
	    $(SED) $(DEPENDENCY_TARGET_SED_PATTERN) $$($1_DEPS_FILE) \
	        > $$($1_DEPS_TARGETS_FILE)
          endif
        else
          # The Visual Studio compiler lacks a feature for generating make
          # dependencies, but by setting -showIncludes, all included files are
          # printed. These are filtered out and parsed into make dependences.
          #
          # Keep as much as possible on one execution line for best performance
          # on Windows. No need to save exit code from compilation since
          # pipefail is always active on Windows.
          ifeq ($$(filter %.S, $$($1_FILENAME)), )
	    $$(call ExecuteWithLog, $$@, $$(call MakeCommandRelative, \
	        $$($1_COMPILER) -showIncludes $$($1_COMPILE_OPTIONS))) \
	        | $(TR) -d '\r' | $(GREP) -v -e "^Note: including file:" \
	            -e "^$$($1_FILENAME)$$$$" || test "$$$$?" = "1" ; \
	    $(ECHO) $$@: \\ > $$($1_DEPS_FILE) ; \
	    $(SED) $(WINDOWS_SHOWINCLUDE_SED_PATTERN) $$($1_OBJ).log \
	        | $(SORT) -u >> $$($1_DEPS_FILE) ; \
	    $(ECHO) >> $$($1_DEPS_FILE) ; \
	    $(SED) $(DEPENDENCY_TARGET_SED_PATTERN) $$($1_DEPS_FILE) > $$($1_DEPS_TARGETS_FILE)
          else
            # For assembler calls just create empty dependency lists
	    $$(call ExecuteWithLog, $$@, $$(call MakeCommandRelative, \
	        $$($1_COMPILER) $$($1_FLAGS) \
	        $(CC_OUT_OPTION)$$($1_OBJ) -Ta $$($1_SRC_FILE))) \
	        | $(TR) -d '\r' | $(GREP) -v -e "Assembling:" || test "$$$$?" = "1" ; \
	    $(ECHO) > $$($1_DEPS_FILE) ; \
	    $(ECHO) > $$($1_DEPS_TARGETS_FILE)
          endif
        endif
  endif
endef

################################################################################
define CreatePrecompiledHeader
  ifneq ($$($1_PRECOMPILED_HEADER), )
    ifeq ($(USE_PRECOMPILED_HEADER), true)
      ifeq ($(TOOLCHAIN_TYPE), microsoft)
        $1_PCH_FILE := $$($1_OBJECT_DIR)/$1.pch
        $1_GENERATED_PCH_SRC := $$($1_OBJECT_DIR)/$1_pch.cpp
        $1_GENERATED_PCH_OBJ := $$($1_OBJECT_DIR)/$1_pch$(OBJ_SUFFIX)

        $$(eval $$(call CreateCompiledNativeFile, $1_$$(notdir $$($1_GENERATED_PCH_SRC)), \
            FILE := $$($1_GENERATED_PCH_SRC), \
            BASE := $1, \
            EXTRA_CXXFLAGS := -Fp$$($1_PCH_FILE) -Yc$$(notdir $$($1_PRECOMPILED_HEADER)), \
        ))

        $1_USE_PCH_FLAGS := \
            -Fp$$($1_PCH_FILE) -Yu$$(notdir $$($1_PRECOMPILED_HEADER))

        $$($1_ALL_OBJS): $$($1_GENERATED_PCH_OBJ)

        # Explicitly add the pch obj file first to ease comparing to old
        # hotspot build.
        $1_ALL_OBJS := $$($1_GENERATED_PCH_OBJ) $$($1_ALL_OBJS)

        $$($1_GENERATED_PCH_SRC):
		$(ECHO) "#include \"$$(notdir $$($1_PRECOMPILED_HEADER))\"" > $$@

      else ifneq ($(findstring $(TOOLCHAIN_TYPE), gcc clang), )
        ifeq ($(TOOLCHAIN_TYPE), gcc)
          $1_PCH_FILE := $$($1_OBJECT_DIR)/precompiled/$$(notdir $$($1_PRECOMPILED_HEADER)).gch
          $1_USE_PCH_FLAGS := -I$$($1_OBJECT_DIR)/precompiled
        else ifeq ($(TOOLCHAIN_TYPE), clang)
          $1_PCH_FILE := $$($1_OBJECT_DIR)/precompiled/$$(notdir $$($1_PRECOMPILED_HEADER)).pch
          $1_USE_PCH_FLAGS := -include-pch $$($1_PCH_FILE)
        endif
        $1_PCH_DEPS_FILE := $$($1_PCH_FILE).d
        $1_PCH_DEPS_TARGETS_FILE := $$($1_PCH_FILE).d.targets

        -include $$($1_PCH_DEPS_FILE)
        -include $$($1_PCH_DEPS_TARGETS_FILE)

        $1_PCH_COMMAND := $$($1_CC) $$($1_CFLAGS) $$($1_EXTRA_CFLAGS) $$($1_SYSROOT_CFLAGS) \
            $$($1_OPT_CFLAGS) -x c++-header -c $(GENDEPS_FLAGS) \
            $$(addsuffix .tmp, $$($1_PCH_DEPS_FILE))

        $$($1_PCH_FILE): $$($1_PRECOMPILED_HEADER) $$($1_COMPILE_VARDEPS_FILE)
		$$(call LogInfo, Generating precompiled header)
		$$(call MakeDir, $$(@D))
		$$(call ExecuteWithLog, $$@, $$(call MakeCommandRelative, \
		    $$($1_PCH_COMMAND) $$< -o $$@))
		$$(call fix-deps-file, $$($1_PCH_DEPS_FILE))
		$(SED) $(DEPENDENCY_TARGET_SED_PATTERN) $$($1_PCH_DEPS_FILE) \
		    > $$($1_PCH_DEPS_TARGETS_FILE)

        $$($1_ALL_OBJS): $$($1_PCH_FILE)

        # Generate the corresponding compile_commands.json fragment.
        $1_PCH_FILE_JSON := $$(MAKESUPPORT_OUTPUTDIR)/compile-commands/$$(subst /,_,$$(subst \
            $$(OUTPUTDIR)/,,$$($1_PCH_FILE))).json
        $1_ALL_OBJS_JSON += $$($1_PCH_FILE_JSON)

        $$($1_PCH_FILE_JSON): $$($1_PRECOMPILED_HEADER) $$($1_COMPILE_VARDEPS_FILE)
		$$(call WriteCompileCommandsFragment, $$@, $$(PWD), $$<, \
		    $$($1_PCH_COMMAND) $$< -o $$($1_PCH_FILE))
      endif
    endif
  endif
endef

################################################################################
define CreateWindowsResourceFile
  ifneq ($$($1_VERSIONINFO_RESOURCE), )
    $1_RES := $$($1_OBJECT_DIR)/$$($1_BASENAME).res
    $1_RES_DEPS_FILE := $$($1_RES).d
    $1_RES_DEPS_TARGETS_FILE := $$($1_RES).d.targets
    -include $$($1_RES_DEPS_FILE)
    -include $$($1_RES_DEPS_TARGETS_FILE)

    $1_RES_VARDEPS := $$($1_RC) $$($1_RCFLAGS)
    $1_RES_VARDEPS_FILE := $$(call DependOnVariable, $1_RES_VARDEPS, \
        $$($1_RES).vardeps)

    $$($1_RES): $$($1_VERSIONINFO_RESOURCE) $$($1_RES_VARDEPS_FILE)
		$$(call LogInfo, Compiling resource $$(notdir $$($1_VERSIONINFO_RESOURCE)) (for $$($1_BASENAME)))
		$$(call MakeDir, $$(@D) $$($1_OBJECT_DIR))
		$$(call ExecuteWithLog, $$@, $$(call MakeCommandRelative, \
		    $$($1_RC) $$($1_RCFLAGS) $$($1_SYSROOT_CFLAGS) $(CC_OUT_OPTION)$$@ \
		    $$($1_VERSIONINFO_RESOURCE) 2>&1 ))
                # Windows RC compiler does not support -showIncludes, so we mis-use CL
                # for this. Filter out RC specific arguments that are unknown to CL.
                # For some unknown reason, in this case CL actually outputs the show
                # includes to stderr so need to redirect it to hide the output from the
                # main log.
		$$(call ExecuteWithLog, $$($1_RES_DEPS_FILE)$(OBJ_SUFFIX), \
		    $$($1_CC) $$(filter-out -l%, $$($1_RCFLAGS)) \
		        $$($1_SYSROOT_CFLAGS) -showIncludes -nologo -TC \
		        $(CC_OUT_OPTION)$$($1_RES_DEPS_FILE)$(OBJ_SUFFIX) -P -Fi$$($1_RES_DEPS_FILE).pp \
		        $$($1_VERSIONINFO_RESOURCE)) 2>&1 \
		    | $(TR) -d '\r' | $(GREP) -v -e "^Note: including file:" \
		        -e "^$$(notdir $$($1_VERSIONINFO_RESOURCE))$$$$" || test "$$$$?" = "1" ; \
		$(ECHO) $$($1_RES): \\ > $$($1_RES_DEPS_FILE) ; \
		$(SED) $(WINDOWS_SHOWINCLUDE_SED_PATTERN) $$($1_RES_DEPS_FILE)$(OBJ_SUFFIX).log \
		    >> $$($1_RES_DEPS_FILE) ; \
		$(ECHO) >> $$($1_RES_DEPS_FILE) ; \
		$(SED) $(DEPENDENCY_TARGET_SED_PATTERN) $$($1_RES_DEPS_FILE) \
		    > $$($1_RES_DEPS_TARGETS_FILE)
  endif
endef