#
# 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 is the top-level entry point for our native compilation and linking.
# It contains the SetupNativeCompilation macro, but is supported by helper
# macros in the make/common/native directory.
################################################################################

ifndef _NATIVE_COMPILATION_GMK
_NATIVE_COMPILATION_GMK := 1

ifeq ($(_MAKEBASE_GMK), )
  $(error You must include MakeBase.gmk prior to including NativeCompilation.gmk)
endif

include native/CompileFile.gmk
include native/DebugSymbols.gmk
include native/Flags.gmk
include native/Link.gmk
include native/LinkMicrosoft.gmk
include native/Paths.gmk

################################################################################
# Setup make rules for creating a native binary (a shared library or an
# executable).
#
# Parameter 1 is the name of the rule. This name is used as variable prefix,
# and the targets generated are listed in a variable by that name.
#
# Remaining parameters are named arguments. These include:
#   NAME The base name for the resulting binary, excluding decorations (like *.exe)
#   TYPE Type of binary (EXECUTABLE, LIBRARY or STATIC_LIBRARY). Default is LIBRARY.
#   SUFFIX Override the default suffix for the output file
#   TARGET_TYPE The type to target, BUILD or TARGET. Defaults to TARGET.
#   LINK_TYPE The language to use for the linker, C or C++. Defaults to C.
#   SRC one or more directory roots to scan for C/C++ files.
#   CFLAGS the compiler flags to be used, used both for C and C++.
#   CXXFLAGS the compiler flags to be used for c++, if set overrides CFLAGS.
#   LDFLAGS the linker flags to be used, used both for C and C++.
#   LDFLAGS_<toolchain> the linker flags to be used for the specified toolchain,
#       used both for C and C++.
#   LDFLAGS_<OS> the linker flags to be used for the specified target OS,
#       used both for C and C++.
#   LDFLAGS_<toolchain>_<OS> the linker flags to be used for the specified
#       toolchain and target OS, used both for C and C++.
#   LIBS the libraries to link to
#   LIBS_<OS> the libraries to link to for the specified target OS,
#       used both for C and C++.
#   LIBS_<toolchain> the libraries to link to for the specified toolchain,
#       used both for C and C++.
#   LIBS_<toolchain>_<OS> the libraries to link to for the specified target
#       OS and toolchain, used both for C and C++.
#   ARFLAGS the archiver flags to be used on unix platforms
#   LIBFLAGS the flags for the lib tool used on windows
#   OBJECT_DIR the directory where we store the object files
#   OUTPUT_DIR the directory where the resulting binary is put
#   SYMBOLS_DIR the directory where the debug symbols are put, defaults to OUTPUT_DIR
#   INCLUDES only pick source from these directories
#   EXCLUDES do not pick source from these directories
#   INCLUDE_FILES only compile exactly these files!
#   EXCLUDE_FILES with these names
#   EXCLUDE_PATTERN exclude files matching any of these substrings
#   EXTRA_FILES List of extra files not in any of the SRC dirs
#   EXTRA_OBJECT_FILES List of extra object files to include when linking
#   EXTRA_DEPS List of extra dependencies to be added to each compiled file
#   VERSIONINFO_RESOURCE Input file for RC. Setting this implies that RC will be run
#   RCFLAGS flags for RC.
#   EMBED_MANIFEST if true, embed manifest on Windows.
#   CC the C compiler to use
#   CXX the C++ compiler to use
#   LD the Linker to use
#   AR the static linker to use
#   LIB the Windows lib tool to use for creating static libraries
#   AS the assembler to use
#   MT the Windows MT tool to use
#   RC the Windows RC tool to use
#   OBJCOPY the objcopy tool for debug symbol handling
#   STRIP the tool to use for stripping debug symbols
#   SYSROOT_CFLAGS the compiler flags for using the specific sysroot
#   SYSROOT_LDFLAGS the linker flags for using the specific sysroot
#   OPTIMIZATION sets optimization level to NONE, LOW, HIGH, HIGHEST, HIGHEST_JVM, SIZE
#   DISABLED_WARNINGS_<toolchain> Disable the given warnings for the specified toolchain
#   DISABLED_WARNINGS_<toolchain>_<OS> Disable the given warnings for the specified
#       toolchain and target OS
#   DISABLED_WARNINGS_C_<toolchain> Disable the given warnings for the specified toolchain
#       when compiling C code
#   DISABLED_WARNINGS_C_<toolchain>_<OS> Disable the given warnings for the specified
#       toolchain and target OS when compiling C code
#   DISABLED_WARNINGS_CXX_<toolchain> Disable the given warnings for the specified
#       toolchain when compiling C++ code
#   DISABLED_WARNINGS_CXX_<toolchain>_<OS> Disable the given warnings for the specified
#       toolchain and target OS when compiling C++ code
#   DISABLED_WARNINGS_<toolchain>_<filename> Disable the given warnings for the specified
#       toolchain when compiling the file specified by filename
#   DISABLED_WARNINGS_<toolchain>_<OS>_<filename> Disable the given warnings for the specified
#       toolchain and target OS when compiling the file specified by filename
#   STRIP_SYMBOLS Set to false to override global strip policy and always leave
#       symbols in the binary, if the toolchain allows for it
#   DEBUG_SYMBOLS Set to false to disable generation of debug symbols
#   COPY_DEBUG_SYMBOLS Set to false to override global setting of debug symbol copying
#   ZIP_EXTERNAL_DEBUG_SYMBOLS Set to false to override global setting of debug symbol
#       zipping
#   STRIPFLAGS Optionally change the flags given to the strip command
#   PRECOMPILED_HEADER Header file to use as precompiled header
#   PRECOMPILED_HEADER_EXCLUDE List of source files that should not use PCH
#   BUILD_INFO_LOG_MACRO Overrides log level of the build info log message, default LogWarn
#   STATIC_LIB_EXCLUDE_OBJS exclude objects that matches from static library
#
# After being called, some variables are exported from this macro, all prefixed
# with parameter 1 followed by a '_':
#   TARGET The library or executable created by the macro
#   TARGET_DEPS All prerequisites for the target calculated by the macro
#   ALL_OBJS All object files
#   IMPORT_LIBRARY The import library created for a shared library on Windows
#
SetupNativeCompilation = $(NamedParamsMacroTemplate)
define SetupNativeCompilationBody
  # When reading this code, note that macros named Setup<Foo> are just setting
  # variables, and macros called Create<Foo> are setting up rules to create
  # files. Macros starting with any other verb are more complicated, and can do
  # all of the above, and also call directly to the shell.

  ###
  ### Prepare for compilation and linking
  ###

  $$(eval $$(call VerifyArguments,$1))

  # Setup variables for the rest of this macro to work with
  $$(eval $$(call SetupBasicVariables,$1))

  # Setup the toolchain to be used
  $$(eval $$(call SetupToolchain,$1))

  # Find all source files to compile and determine the output object file names
  $$(eval $$(call SetupSourceFiles,$1))
  $$(eval $$(call SetupOutputFiles,$1))

  # Setup CFLAGS/CXXFLAGS based on warnings, optimizations, extra flags etc.
  $$(eval $$(call SetupCompilerFlags,$1))

  # Machinery needed for the build to function properly
  $$(eval $$(call SetupBuildSystemSupport,$1))

  $$(eval $$(call RemoveSuperfluousOutputFiles,$1))

  # Need to make sure TARGET is first on list before starting to create files
  $1 := $$($1_TARGET)

  # Have make print information about the library when we start compiling
  $$(eval $$(call PrintStartInfo,$1))

  ###
  ### Compile all native source code files
  ###

  # Create a PCH, if requested
  $$(eval $$(call CreatePrecompiledHeader,$1))

  # Now call CreateCompiledNativeFile for each source file we are going to compile.
  $$(foreach file, $$($1_SRCS), \
      $$(eval $$(call CreateCompiledNativeFile,$1_$$(notdir $$(file)),\
          FILE := $$(file), \
          BASE := $1, \
      )) \
  )

  ifeq ($(call isTargetOs, windows), true)
    # On windows we need to create a resource file
    $$(eval $$(call CreateWindowsResourceFile,$1))
  endif

  # Setup a library-wide dependency file from individual object file dependency
  # files, and import it in the makefile.
  $$(eval $$(call CreateDependencyFile,$1))
  $$(eval $$(call ImportDependencyFile,$1))

  ###
  ### Link the object files into a native output library/executable
  ###

  # Handle native debug symbols
  $$(eval $$(call CreateDebugSymbols,$1))

  # Prepare for linking
  $$(eval $$(call SetupLinkerFlags,$1))
  ifneq ($(TOOLCHAIN_TYPE), microsoft)
    $$(eval $$(call SetupLinking,$1))
  endif

  $$(eval $$(call SetupObjectFileList,$1))

  # Link the individually compiled files into a single unit
  ifneq ($(TOOLCHAIN_TYPE), microsoft)
    $$(eval $$(call CreateLinkedResult,$1))
  else
    $$(eval $$(call CreateLinkedResultMicrosoft,$1))
  endif

  ifeq ($(GENERATE_COMPILE_COMMANDS_ONLY), true)
    # Override all targets (this is a hack)
    $1 := $$($1_ALL_OBJS_JSON)
  endif
endef

################################################################################
# Verify that user passed arguments are valid
define VerifyArguments
  ifneq ($$($1_NAME), $(basename $$($1_NAME)))
    $$(error NAME must not contain any directory path in $1)
  endif
  ifneq ($(findstring $$($1_SUFFIX), $$($1_NAME)), )
    $$(error NAME should be specified without suffix: $$($1_SUFFIX) in $1)
  endif
  ifneq ($(findstring $$($1_PREFIX), $$($1_NAME)), )
    $$(error NAME should be specified without prefix: $$($1_PREFIX) in $1)
  endif
  ifeq ($$($1_OUTPUT_DIR), )
    $$(error OUTPUT_DIR is missing in $1)
  endif
  ifneq ($$($1_MANIFEST), )
    ifeq ($$($1_MANIFEST_VERSION), )
      $$(error If MANIFEST is provided, then MANIFEST_VERSION is required in $1)
    endif
  endif
endef

################################################################################
# Setup basic variables
define SetupBasicVariables
  # If type is unspecified, default to LIBRARY
  ifeq ($$($1_TYPE), )
    $1_TYPE := LIBRARY
  endif

  # If we're doing a static build and producing a library
  # force it to be a static library and remove the -l libraries
  ifeq ($(STATIC_BUILD), true)
    ifeq ($$($1_TYPE), LIBRARY)
      $1_TYPE := STATIC_LIBRARY
    endif
  endif

  # STATIC_LIBS is set from Main.gmk when building static versions of certain
  # native libraries.
  ifeq ($(STATIC_LIBS), true)
    $1_TYPE := STATIC_LIBRARY
    # The static versions need to be redirected to different output dirs, both
    # to not interfere with the main build as well as to not end up inside the
    # jmods.
    $1_OBJECT_DIR := $$($1_OBJECT_DIR)/static
    $1_OUTPUT_DIR := $$($1_OBJECT_DIR)
  endif

  ifeq ($$($1_TYPE), EXECUTABLE)
    $1_PREFIX :=
    ifeq ($$($1_SUFFIX), )
      $1_SUFFIX := $(EXECUTABLE_SUFFIX)
    endif
  else
    $1_PREFIX := $(LIBRARY_PREFIX)
    ifeq ($$($1_TYPE), LIBRARY)
      ifeq ($$($1_SUFFIX), )
        $1_SUFFIX := $(SHARED_LIBRARY_SUFFIX)
      endif
    else ifeq ($$($1_TYPE), STATIC_LIBRARY)
      ifeq ($$($1_SUFFIX), )
        $1_SUFFIX := $(STATIC_LIBRARY_SUFFIX)
      endif
    endif
  endif

  $1_BASENAME := $$($1_PREFIX)$$($1_NAME)$$($1_SUFFIX)
  $1_TARGET := $$($1_OUTPUT_DIR)/$$($1_BASENAME)
  $1_NOSUFFIX := $$($1_PREFIX)$$($1_NAME)
  $1_SAFE_NAME := $$(strip $$(subst /,_, $1))
endef

################################################################################
# Setup the toolchain variables
define SetupToolchain
  ifeq ($$($1_TARGET_TYPE), BUILD)
    $$(call SetIfEmpty, $1_CC, $$(BUILD_CC))
    $$(call SetIfEmpty, $1_CXX, $$(BUILD_CXX))
    $$(call SetIfEmpty, $1_AR, $$(BUILD_AR))
    $$(call SetIfEmpty, $1_LIB, $$(BUILD_LIB))
    $$(call SetIfEmpty, $1_AS, $$(BUILD_AS))
    $$(call SetIfEmpty, $1_OBJCOPY, $$(BUILD_OBJCOPY))
    $$(call SetIfEmpty, $1_STRIP, $$(BUILD_STRIP))
    $$(call SetIfEmpty, $1_SYSROOT_CFLAGS, $$(BUILD_SYSROOT_CFLAGS))
    $$(call SetIfEmpty, $1_SYSROOT_LDFLAGS, $$(BUILD_SYSROOT_LDFLAGS))
    ifeq ($$($1_LINK_TYPE), C++)
      $$(call SetIfEmpty, $1_LD, $$(BUILD_LDCXX))
    else
      $$(call SetIfEmpty, $1_LD, $$(BUILD_LD))
    endif
  else
    $$(call SetIfEmpty, $1_CC, $$(CC))
    $$(call SetIfEmpty, $1_CXX, $$(CXX))
    $$(call SetIfEmpty, $1_AR, $$(AR))
    $$(call SetIfEmpty, $1_LIB, $$(LIB))
    $$(call SetIfEmpty, $1_AS, $$(AS))
    $$(call SetIfEmpty, $1_MT, $$(MT))
    $$(call SetIfEmpty, $1_RC, $$(RC))
    $$(call SetIfEmpty, $1_OBJCOPY, $$(OBJCOPY))
    $$(call SetIfEmpty, $1_STRIP, $$(STRIP))
    $$(call SetIfEmpty, $1_SYSROOT_CFLAGS, $$(SYSROOT_CFLAGS))
    $$(call SetIfEmpty, $1_SYSROOT_LDFLAGS, $$(SYSROOT_LDFLAGS))
    ifeq ($$($1_LINK_TYPE), C++)
      $$(call SetIfEmpty, $1_LD, $$(LDCXX))
    else
      $$(call SetIfEmpty, $1_LD, $$(LD))
    endif
  endif
endef

################################################################################
# Setup machinery needed by the build system
define SetupBuildSystemSupport
  # Track variable changes for all variables that affect the compilation command
  # lines for all object files in this setup. This includes at least all the
  # variables used in the call to add_native_source below.
  $1_COMPILE_VARDEPS := $$($1_CFLAGS) $$($1_EXTRA_CFLAGS) $$($1_SYSROOT_CFLAGS) \
      $$($1_CXXFLAGS) $$($1_EXTRA_CXXFLAGS) $$($1_OPT_CFLAGS) $$($1_OPT_CXXFLAGS) \
      $$($1_CC) $$($1_CXX) $$($1_AS) $$($1_ASFLAGS)
  $1_COMPILE_VARDEPS_FILE := $$(call DependOnVariable, $1_COMPILE_VARDEPS, \
      $$($1_OBJECT_DIR)/$$($1_NOSUFFIX).comp.vardeps)
endef

################################################################################
# Have make print information about the library when we start compiling
define PrintStartInfo
  # Setup rule for printing progress info when compiling source files.
  # This is a rough heuristic and may not always print accurate information.
  # The $1_BUILD_INFO and $1_BUILD_INFO_DEPS variables are used in
  # TestFilesCompilation.gmk.
  $$(call SetIfEmpty, $1_BUILD_INFO_LOG_MACRO, LogWarn)
  $1_BUILD_INFO_DEPS := $$($1_SRCS) $$($1_COMPILE_VARDEPS_FILE)
  $1_BUILD_INFO := $$($1_OBJECT_DIR)/_build-info.marker

  $$($1_BUILD_INFO): $$($1_BUILD_INFO_DEPS)
        ifeq ($$(wildcard $$($1_TARGET)), )
	  $$(call $$($1_BUILD_INFO_LOG_MACRO), \
	      Creating $$(subst $$(OUTPUTDIR)/,,$$($1_TARGET)) from $$(words \
	      $$(filter-out %.vardeps, $$?)) file(s))
        else
	  $$(call $$($1_BUILD_INFO_LOG_MACRO), \
	      $$(strip Updating $$(subst $$(OUTPUTDIR)/,,$$($1_TARGET)) \
	      $$(if $$(filter-out %.vardeps, $$?), \
	        due to $$(words $$(filter-out %.vardeps, $$?)) file(s), \
	      $$(if $$(filter %.vardeps, $$?), due to makefile changes))))
        endif
	$(TOUCH) $$@
endef

################################################################################
# Setup a library-wide dependency file from individual object file dependency
# files
define CreateDependencyFile
  # Create a rule to collect all the individual make dependency files into a
  # single makefile.
  $1_DEPS_FILE := $$($1_OBJECT_DIR)/$1.d

  $$($1_DEPS_FILE): $$($1_ALL_OBJS) $$($1_RES)
	$(RM) $$@
        # CD into dir to reduce risk of hitting command length limits, which
        # could otherwise happen if TOPDIR is a very long path.
	$(CD) $$($1_OBJECT_DIR) && $(CAT) *.d > $$@.tmp
	$(CD) $$($1_OBJECT_DIR) && $(CAT) *.d.targets | $(SORT) -u >> $$@.tmp
        # After generating the file, which happens after all objects have been
        # compiled, copy it to .old extension. On the next make invocation, this
        # .old file will be included by make.
	$(CP) $$@.tmp $$@.old
	$(MV) $$@.tmp $$@

  $1 += $$($1_DEPS_FILE)
endef

################################################################################
# Import the dependency file into the makefile
define ImportDependencyFile
  # The include must be on the .old file, which represents the state from the
  # previous invocation of make. The file being included must not have a rule
  # defined for it as otherwise make will think it has to run the rule before
  # being able to include the file, which would be wrong since we specifically
  # need the file as it was generated by a previous make invocation.
  ifneq ($$(wildcard $$($1_DEPS_FILE).old), )
    $1_DEPS_FILE_LOADED := true
    -include $$($1_DEPS_FILE).old
  endif
endef

endif # _NATIVE_COMPILATION_GMK