# -*- Makefile -*-
# vim:set ft=make:
#
# L4Re Buildsystem
#
# Make configuration file
#
# This file is included by all Makefile-templates. This file defines macros
# for div commands, sets general DROPS-Makefile variables, ensures the
# dependencies from the various Makeconf.locals, defines the messages for
# div actions, and permits the dependency-creation on clean-rules.
#

ifeq ($(origin _L4DIR_MK_MAKECONF),undefined)
_L4DIR_MK_MAKECONF=y

include $(L4DIR)/mk/util.mk

MAKEFLAGS        += --no-print-directory -Rr

# the default target is all
all::

mrproper:: cleanall

# make .general.d dependent on the role-file
$(if $(ROLE),$(OBJ_DIR)/.general.d: $(L4DIR)/mk/$(ROLE))

-include $(wildcard $(L4DIR)/mk/arch/Makeconf.*)

# Additional available variants, 'std' is always available
VARIANTS_AVAILABLE := $(patsubst $(L4DIR)/mk/variants/%.inc,%,$(wildcard $(L4DIR)/mk/variants/*.inc))

include $(foreach v,$(VARIANTS_AVAILABLE),$(L4DIR)/mk/variants/$(v).inc)

CHOSEN_VARIANTS := $(subst +, ,$(VARIANT))

# Get variant specific variables for all chosen variants. This will for
# example get <VARNAME>-variant-nofpu if nofpu variant is a chosen variant
define variant_values
  $(foreach v, $(CHOSEN_VARIANTS), $($(1)-variant-$(v)))
endef

$(foreach v, $(CHOSEN_VARIANTS), $(eval $(BID_DEFINE-variant-$(v))))

ARCH = $(BUILD_ARCH)

BUILDDIR_SEARCHPATH = $(OBJ_BASE)/bin/$(ARCH)_$(CPU)/plain:$(OBJ_BASE)/bin/$(ARCH)_$(CPU)/$(BUILD_ABI):$(OBJ_BASE)/lib/$(ARCH)_$(CPU)/std/plain:$(OBJ_BASE)/lib/$(ARCH)_$(CPU)/std/$(BUILD_ABI):$(OBJ_BASE)/assets

CROSS_COMPILE    ?= $(CROSS_COMPILE_$(ARCH))

OFORMAT           = $(OFORMAT_$(ARCH))
BFD_ARCH          = $(BFD_ARCH_$(ARCH))
L4_KIP_ADDR         ?= $(L4_KIP_ADDR_$(ARCH))

L4_KIP_OFFS_SYS_INVOKE   = 0x800
L4_KIP_OFFS_SYS_DEBUGGER = 0x900
L4_STACK_ADDR           ?= $(L4_STACK_ADDR_$(ARCH))
L4_STACK_SIZE           ?= $(if $(L4_STACK_SIZE_MAIN_THREAD),$(L4_STACK_SIZE_MAIN_THREAD),0x8000)

CC_WHITELIST-gcc     := 9 10 11 12 13 14 15
CC_WHITELIST-clang   := 15 16 17 18 19

VERBOSE          = $(if $(CONFIG_VERBOSE),,@)
verbcmd          = $(if $(CONFIG_VERBOSE),,@)$1 $2 $3
DEPEND_VERBOSE   = $(if $(CONFIG_DEPEND_VERBOSE),,@)
DROPS_STDDIR     = $(patsubst "%",%,$(CONFIG_DROPS_STDDIR))
DROPS_INSTDIR    = $(patsubst "%",%,$(CONFIG_DROPS_INSTDIR))
RAM_SIZE_MB      = $(CONFIG_RAM_SIZE_MB)
PLATFORM_TYPE   ?= $(patsubst "%",%,$(CONFIG_PLATFORM_TYPE))
BITS             = $(patsubst "%",%,$(CONFIG_BITS))
CPU              = $(patsubst "%",%,$(CONFIG_CPU))
CPU_ABI          = $(patsubst "%",%,$(CONFIG_CPU_ABI))
BUILD_ABI        = $(patsubst "%",%,$(CONFIG_BUILD_ABI))
BUILD_ARCH       = $(patsubst "%",%,$(CONFIG_BUILD_ARCH))
MAKECONFS_ADD    = $(call strip_quotes,$(CONFIG_MAKECONFS_ADD))

CARCHFLAGS       = $(CARCHFLAGS_$(ARCH)) $(CARCHFLAGS_$(ARCH)_$(CPU))
CCXX_FLAGS       = $(CCXX_FLAGS_$(BUILD_ARCH))

# Prefix tool with 'g' to get the GNU tool (via Homebrew)
gnutool          = $(if $(filter darwin,$(HOST_SYSTEM)),g)$(1)

error_on_env_vars = $(if $(filter environment,$(origin $1)), \
                         $(error '$1' specified in environment. \
                         Please use a Makeconf.local file for \
                         specifying tool overrides explicitly))

dv               = $(call error_on_env_vars,$1)$\
                   $(if $(filter-out default undefined,$(origin $(1))),$(1)-default,$(1))

$(call dv,AR)	 = $(CROSS_COMPILE)ar
$(call dv,AS)	 = $(CROSS_COMPILE)as
AWKP		?= gawk --posix
CCACHE          ?= ccache
$(call dv,FC)    = $(if $(USE_CCACHE),$(CCACHE) )$(CROSS_COMPILE)gfortran

# Args: 1=$(CLANG); 2=clang/clang++/clang-cpp
# Rules:
#  If      $(CLANG) contains '-', use $(CLANG) as clang-version
#  else if $(CLANG) contains '/', use $(CLANG) as clang-path
#  otherwise clang-version/clang-path are both empty.
# The $\ is required to suppress a newline in the command for $(CLANG) without
# '-', see the comparison in BID_call_compiler_default.
clang-bin        = $(if $(filter -%,$(1)),$(2)$(1),$\
		     $(if $(filter %/,$(1)),$(1)$(2),$(2)))
clang-bin-tgt    = $(call clang-bin,$(1),$(2)) \
		   $(addprefix --target=,$(notdir $(CROSS_COMPILE:%-=%)))

$(call dv,CC)    = $(if $(USE_CCACHE),$(CCACHE) )$(if $(CLANG),$(call \
                     clang-bin-tgt,$(CLANG),clang),$(CROSS_COMPILE)gcc)
$(call dv,CXX)   = $(if $(USE_CCACHE),$(CCACHE) )$(if $(CLANG),$(call \
                     clang-bin-tgt,$(CLANG),clang++),$(CROSS_COMPILE)g++)
$(call dv,CPP)   = $(if $(CLANG),$(call \
                     clang-bin-tgt,$(CLANG),clang-cpp),$(CROSS_COMPILE)cpp)
$(call dv,ADAC)  = $(CROSS_COMPILE)gnatmake -q
HOST_ADAC        = gnatmake -q

CP		?= $(call gnutool,cp)

DOXYGEN		?= doxygen
ECHO		?= echo
ELF_PATCHER     = $(OBJ_BASE)/tool/elf-patcher/elf-patcher
FIND            = $(call gnutool,find)
FIXDEP          = $(OBJ_BASE)/scripts/basic/fixdep

GENOFFSETS	= $(L4DIR)/tool/bin/genoffsets.pl
GOSH		= $(firstword $(wildcard $(L4DIR)/../tools/gosh/gosh \
				$(DROPS_STDDIR)/tool/bin/gosh \
				 $(shell which gosh 2>/dev/null) ) \
				 did_not_find_gosh___please_install_gosh )
HOST_CC		?= $(if $(CLANG),$(call clang-bin,$(CLANG),clang),gcc)
HOST_CXX	?= $(if $(CLANG),$(call clang-bin,$(CLANG),clang++),g++)
HOST_LD		?= ld
INSTALL		= install
$(call dv,LD)   = $(CROSS_COMPILE)ld -m $(LD_EMULATION) --oformat $(OFORMAT)
DTC		?= dtc
LATEX		= latex
PDFLATEX	= pdflatex
GREP		= GREP_OPTIONS= grep
LN		= $(call gnutool,ln)
MKDIR		= mkdir -p
NM		?= $(CROSS_COMPILE)nm
OBJCOPY		?= $(CROSS_COMPILE)objcopy
OBJDUMP		?= $(CROSS_COMPILE)objdump
RANLIB		?= $(CROSS_COMPILE)ranlib
$(call dv,RM)   = rm -f
SCRUB		= $(RM) $(wildcard *.old) $(wildcard *~) $(wildcard *.bak) \
		        $(wildcard \#*\#)
SED		= $(call gnutool,sed)
SHELL		= bash
SIZE		?= $(CROSS_COMPILE)size
STRIP		?= $(CROSS_COMPILE)strip
SVN		= svn
TR		= tr
GEN_DOPECODE	= $(L4DIR)/tool/gen_dopecode/gen_dopecode
PAGER		?= less
DISASM_CMD      ?= $(OBJDUMP) -lCSd $(1) | $(PAGER)
IMAGES_DIR      ?= $(OBJ_BASE)/images

# QEMU defaults
QEMU_ARCH_MAP_$(ARCH) ?= qemu-system-$(ARCH)
QEMU_PATH             ?= $(QEMU_ARCH_MAP_$(ARCH))

# Arm FVP defaults
FVP_PLAT_MAP_arm_fvp_base   ?= FVP_Base_RevC-2xAEMvA
FVP_PLAT_MAP_arm_fvp_base_r ?= FVP_BaseR_AEMv8R
FVP_PATH                    ?= $(FVP_PLAT_MAP_$(PLATFORM_TYPE))

# Subdirs where libs and binaries are put into
BID_install_subdir_base = $(ARCH)_$(CPU)/$(L4API)
BID_install_subdir_var  = $(subst -,/,$(SYSTEM))

ifneq ($(PT),)
  PLATFORM_TYPE := $(PT)
endif

# include this one early to be able to set OBJ_BASE
-include $(L4DIR)/Makeconf.local
-include $(L4DIR)/conf/Makeconf.local

# output directory
ifeq ($(O)$(OBJ_BASE),)
 $(error need to give builddir with O=.../builddir)
else
 ifneq ($(O),)
  ifeq ($(filter-out undefined environment,$(origin OBJ_BASE)),)
   OBJ_BASE := $(abspath $(O))
   export OBJ_BASE
   # prevent passing O to sub-makes, because it may be a relative path
   # not valid there
   override O =
   MAKEOVERRIDES := $(filter-out O=%,$(MAKEOVERRIDES))
  endif
 endif
endif

ifeq ($(origin L4DIR_ABS),undefined)
L4DIR_ABS      := $(abspath $(L4DIR))
endif
ifeq ($(origin PKGDIR_ABS),undefined)
PKGDIR_ABS     := $(abspath $(PKGDIR))
endif
ifeq ($(origin SRC_DIR),undefined)
SRC_DIR        := $(CURDIR)
endif
ifeq ($(origin SRC_BASE_ABS),undefined)
SRC_BASE     ?= $(L4DIR)
SRC_BASE_ABS := $(abspath $(SRC_BASE))
export SRC_BASE_ABS
endif
ifeq ($(origin OBJ_DIR),undefined)
OBJ_DIR        := $(subst $(SRC_BASE_ABS),$(OBJ_BASE),$(SRC_DIR))
endif
ifeq ($(origin PKGDIR_OBJ),undefined)
PKGDIR_OBJ     := $(abspath $(OBJ_DIR)/$(PKGDIR))
endif

PKGDIR_REL     := $(patsubst pkg/%,%,$(patsubst $(L4DIR_ABS)/%,%,$(PKGDIR_ABS)))

# if PKGDIR is not in L4DIR, we have an external package, so make up some
# build-dir for it
ifneq ($(patsubst $(L4DIR_ABS)/%,,$(PKGDIR_ABS)),)
ifneq ($(filter-out $(OBJ_BASE)/ext-pkg%,$(PKGDIR_OBJ)),)
PKGDIR_OBJ     := $(OBJ_BASE)/ext-pkg$(PKGDIR_OBJ)
OBJ_DIR        := $(OBJ_BASE)/ext-pkg$(OBJ_DIR)
endif
endif

# sanity check the object dir
ifneq ($(SRC_BASE_ABS),$(OBJ_BASE))
ifeq ($(SRC_DIR),$(OBJ_DIR))
$(warning Sorry, your object or source path became garbled.)
$(warning OBJ_BASE: $(OBJ_BASE))
$(warning SRC_BASE_ABS: $(SRC_BASE_ABS))
$(warning SRC_DIR: $(SRC_DIR))
$(warning OBJ_DIR: $(OBJ_DIR))
$(warning PKGDIR: $(PKGDIR))
$(warning L4DIR_ABS: $(L4DIR_ABS))
$(warning PKGDIR_ABS: $(PKGDIR_ABS))
$(warning PKGDIR_OBJ: $(PKGDIR_OBJ))
$(error Please investigate.)
endif
endif


OBJ_DIR_EXPORT = $(OBJ_DIR)
export OBJ_DIR_EXPORT

VPATH_SRC_BASE ?= $(SRC_DIR)

# Makeconf.local handling
# dont use -include here, as we have special build conditions in $(L4DIR)/
ifeq ($(origin BID_ROOT_CONF),undefined)
BID_ROOT_CONF := $(abspath $(OBJ_BASE))/.config.all
endif
ifeq ($(wildcard $(BID_ROOT_CONF)),)
 ifeq ($(BID_IGN_ROOT_CONF),)
$(error No configuration file found in build directory "$(OBJ_BASE)". Please run "make O=/path/to/objdir config" in "$(L4DIR_ABS)" or specify a valid build directory)
 endif
else
 include $(BID_ROOT_CONF)
endif

# If we're working on a program that wants the RAM_BASE be considered in its
# linking address, source a possible privately configured one. Without MMU,
# we naturally need RAM_BASE.
ifneq ($(if $(CONFIG_MMU),$(RELOC_PHYS),y),)
INCLUDE_BOOT_CONFIG := required
endif

ifneq ($(INCLUDE_BOOT_CONFIG),)
 -include $(L4DIR)/conf/Makeconf.boot
 -include $(OBJ_BASE)/conf/Makeconf.boot
 PLATFORM_CONF_FILE := $(firstword $(wildcard $(L4DIR)/conf/platforms/$(PLATFORM_TYPE).conf $(L4DIR)/mk/platforms/$(PLATFORM_TYPE).conf))
 ifneq ($(PLATFORM_TYPE),$(patsubst "%",%,$(CONFIG_PLATFORM_TYPE)))
  include $(PLATFORM_CONF_FILE)
 else
  ifneq ($(CONFIG_PLATFORM_TYPE_custom),)
   PLATFORM_RAM_BASE=$(CONFIG_PLATFORM_RAM_BASE)
   PLATFORM_RAM_SIZE_MB=$(CONFIG_PLATFORM_RAM_SIZE_MB)
   PLATFORM_UART_NR=$(CONFIG_PLATFORM_UART_NR)
  else
   ifeq ($(INCLUDE_BOOT_CONFIG),optional)
    -include $(PLATFORM_CONF_FILE)
   else
    include $(PLATFORM_CONF_FILE)
   endif
  endif
 endif
 -include $(OBJ_BASE)/Makeconf.ram_base
 BID_RAM_BASE_DEP := $(if $(wildcard $(OBJ_BASE)/Makeconf.ram_base),$(OBJ_BASE)/Makeconf.ram_base)
 ifeq ($(RAM_BASE),)
  RAM_BASE := 0
 endif
 RAM_SIZE_MB := $(if $(RAM_SIZE_MB),$(RAM_SIZE_MB),$(PLATFORM_RAM_SIZE_MB))
endif

INCLUDE_MAKE_RULES += $(foreach m,$(MAKECONFS_ADD),$(SRC_DIR)/Makeconf.$(m))

INCLUDE_MAKE_RULES_EXPANDED := $(foreach m,$(INCLUDE_MAKE_RULES),$(wildcard $(m)))
ifneq ($(strip $(INCLUDE_MAKE_RULES_EXPANDED)),)
-include $(INCLUDE_MAKE_RULES_EXPANDED)
endif

-include $(OBJ_BASE)/Makeconf.local
-include $(OBJ_BASE)/conf/Makeconf.local
ifneq ($(PKGDIR_ABS),)
-include $(PKGDIR_ABS)/Makeconf.local
endif
# if it is not already set, we use this in the local dir
MAKECONFLOCAL ?= Makeconf.local
-include $(MAKECONFLOCAL)

define _check_toolpath
 ifneq ($(CONFIGURED_$1),)
  ifneq ($(CONFIGURED_$1),$(realpath $(shell which $(firstword $(filter-out $(CCACHE),$($1))))))
   $$(error Configured $1 different than used one:$$(newline)       \
            Configured: $(CONFIGURED_$1)$$(newline)                 \
            Used:       $(realpath $(shell which $(firstword $(filter-out $(CCACHE),$($1)))))$$(newline) \
            ==> Please reconfigure by calling "make oldconfig" in the root source directory with the same compiler set)
  endif
 endif
endef

ifeq ($(BID_IGN_ROOT_CONF),)
 $(eval $(call _check_toolpath,CC))
 $(eval $(call _check_toolpath,CXX))
endif

DROPS_STDDIR	?= /home/drops

QEMU_OPTIONS          ?= -serial stdio $(QEMU_OPTIONS_$(ARCH))

ifneq ($(PL),)
PL_j := -j $(PL)
export PL
endif

include $(L4DIR)/mk/config.inc

# MAKEDEP-call:
# arg1 - compiler binary name
# arg2 - [opt] compiler target. Will be written as target within the
# 	       dependency file
# arg3 - [opt] name of the dependency file. If unset, .<arg2>.d will be used.
# arg4 - [opt] alternative binary name
ifeq ($(origin BID_LIBGENDEP_PATHS), undefined)
 ifeq ($(HOST_SYSTEM),linux)
   BID_LIBGENDEP_PATHS_fn = \
     $(firstword $(wildcard $(abspath $(OBJ_BASE)/tool/gendep$(1) \
                                      $(DROPS_STDDIR)/tool/lib$(1))))
   BID_LIBGENDEP_PATHS := \
     $(call BID_LIBGENDEP_PATHS_fn,/64):$(call \
            BID_LIBGENDEP_PATHS_fn,/32):$(call BID_LIBGENDEP_PATHS_fn)
 endif

 ifeq ($(HOST_SYSTEM),darwin)
   BID_LIBGENDEP_PATHS := \
     $(firstword $(wildcard $(abspath $(OBJ_BASE)/tool/gendep \
                                      $(DROPS_STDDIR)/tool/lib)))
 endif
endif

ifeq ($(HOST_SYSTEM),linux)
  LD_GENDEP_PREFIX = LD_PRELOAD=libgendep.so LD_LIBRARY_PATH=$(if $(LD_LIBRARY_PATH),$(LD_LIBRARY_PATH):)$(BID_LIBGENDEP_PATHS)
endif
ifeq ($(HOST_SYSTEM),darwin)
  LD_GENDEP_PREFIX = DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=$(BID_LIBGENDEP_PATHS)/libgendep.so
endif
MAKEDEP=$(LD_GENDEP_PREFIX) \
		GENDEP_TARGET=$(if $(2),$(2),$@) \
		GENDEP_BINARY=$(firstword $(1)) $(if $(3),GENDEP_DEPFILE=$(3)) \
		$(if $(4),GENDEP_BINARY_ALT1=$(4))

# We recognize the following Fortran file extensions.
FORTRAN_FILE_EXTENSIONS = .f .F .f90 .F90 .f95 .F95

# We recognize the following Ada file extensions.
ADA_FILE_EXTENSIONS = .adb .ads

# We recognize the following file extensions for assembly.
BID_ASM_FILE_EXTENSIONS ?= .S
BID_ASM_CPP_FILE_EXTENSIONS ?= .S

# macros used here and in packages

if_for_target = $(if $(filter host,$(MODE)),,$(1))

# check if the current architecture is in the provided list of allowed
# architectures and exit with an error if not.
# 1: list of allowed architectures
define check_for_arch
  $(if $(filter $(ARCH),$1),,
    $(error ERROR: Architecture '$(ARCH)' is not supported for target $@))
endef

# Get all variants of a variable
# This contains specific versions of this variable for
#  - architecture
#  - original system
#  - target
#  - target directory
# As well es their `-y` variants
#
# 1: variable name
define bid_flag_variants
  $($(1)-y) $($(1)_$(ARCH)) $($(1)_$(ARCH)-y)                           \
  $($(1)_$(OSYSTEM)) $($(1)_$(OSYSTEM)-y)                               \
  $($(1)_$@) $($(1)_$@-y) $($(1)_$@_$(OSYSTEM)) $($(1)_$@_$(OSYSTEM)-y) \
  $($(1)_$(@D)) $($(1)_$<) $($(1)_$(@D)/$(<F))                          \
  $($(1)_$(<F)) $($(1)_$(<F)_$(OSYSTEM)) $($(1)_$(<F)_$(OSYSTEM)-y)     \
  $($(1)_$(<D)) $(call variant_values,$(1))
endef

# 1: argument to validate and return if it works
# 2: return if argument does not work with compiler
# 3: compiler name
# 4: compiler type
check_compiler_opt = $(if $(shell $(filter-out $(CCACHE),$(3)) $(CARCHFLAGS) \
                        $(CCXX_FLAGS) $(check_compiler_flags) $(1) -c -o /dev/null \
                        -x $(4) /dev/null >/dev/null 2>&1 || echo X),$(2),$(1))
checkcc   = $(call check_compiler_opt,$(1),$(2),$(CC),c)
checkcxx  = $(call check_compiler_opt,$(1),$(2),$(CXX),c++)
checkdtc  = $(shell if $(DTC) $(1) --version /dev/null > /dev/null 2>&1; \
                    then echo "$(1)"; fi)
checkcc_nowarn  = $(if $(call checkcc,-W$(1),),-Wno-$(1))
checkcxx_nowarn = $(if $(call checkcxx,-W$(1),),-Wno-$(1))
checkld   = $(shell if $(callld) -v "$1" > /dev/null 2>&1; \
             then echo "$(1)"; else echo "$(2)"; fi)

callcc    = LC_ALL=C $(filter-out $(CCACHE),$(CC))  $(CARCHFLAGS) $(CCXX_FLAGS)
callcxx   = LC_ALL=C $(filter-out $(CCACHE),$(CXX)) $(CARCHFLAGS) $(CCXX_FLAGS)
callfc    = LC_ALL=C $(filter-out $(CCACHE),$(FC))  $(CARCHFLAGS) $(CCXX_FLAGS)
callld    = LC_ALL=C $(firstword $(LD))

get_cc_version_part = $(shell echo $(1) | $(callcc) -E -x c - | tail -1)

# compiler variables: version, base dir, include dir, gcc lib, ...
# despite having GCC in their name, they are generic and also used with and
# derived for clang
# note: determining these variables is slow, and the values should
#       be set in .config.all. However, this is the place were
#       they are determined on a 'make config' in $(L4DIR)
BID_COMPILER_TYPE_f = $(if $(findstring clang, $(shell $(callcc) --version)),clang,gcc)
BID_LD_TYPE_f = $(if $(findstring LLD, $(shell $(callld) --version)),lld,gnu)

# We fall back to BID_COMPILER_TYPE_f if BID_COMPILER_TYPE is not cached yet,
# because we need this info in the following lines before caching
__tmp_bid_compiler_type := $(or $(BID_COMPILER_TYPE),$(BID_COMPILER_TYPE_f))

ifeq ($(__tmp_bid_compiler_type),gcc)
 BID_COMPILER_IS_GCC = 1
endif
ifeq ($(__tmp_bid_compiler_type),clang)
 BID_COMPILER_IS_CLANG = 1
 check_compiler_flags := -Werror=unknown-warning-option -Werror=unused-command-line-argument
endif

GCCMAJORVERSION_ID_gcc   = __GNUC__
GCCMINORVERSION_ID_gcc   = __GNUC_MINOR__
GCCPATCHLEVEL_ID_gcc     = __GNUC_PATCHLEVEL__
GCCMAJORVERSION_ID_clang = __clang_major__
GCCMINORVERSION_ID_clang = __clang_minor__
GCCPATCHLEVEL_ID_clang   = __clang_patchlevel__

GCCMAJORVERSION_ID_f = $(GCCMAJORVERSION_ID_$(BID_COMPILER_TYPE_f))
GCCMINORVERSION_ID_f = $(GCCMINORVERSION_ID_$(BID_COMPILER_TYPE_f))
GCCPATCHLEVEL_ID_f   = $(GCCPATCHLEVEL_ID_$(BID_COMPILER_TYPE_f))

GCCMAJORVERSION_f  = $(call get_cc_version_part, $(GCCMAJORVERSION_ID_f))
GCCMINORVERSION_f  = $(call get_cc_version_part, $(GCCMINORVERSION_ID_f))
GCCPATCHLEVEL_f    = $(call get_cc_version_part, $(GCCPATCHLEVEL_ID_f))

# the version is just the major version except for GCC 1-4 where it is
# major.minor
GCCVERSION_f_gcc   = $(GCCMAJORVERSION_f)$(if $(filter $(GCCMAJORVERSION_f),1 2 3 4),.$(GCCMINORVERSION_f))
GCCVERSION_f_clang = $(GCCMAJORVERSION_f)
GCCVERSION_f       = $(GCCVERSION_f_$(BID_COMPILER_TYPE_f))
GNATVERSION_f      = $(shell LC_ALL=C $(ADAC) --version 2>/dev/null | sed -ne 's/GNATMAKE \([^ ]*\).*/\1/p')
HOST_GNATVERSION_f = $(shell LC_ALL=C $(HOST_ADAC) --version 2>/dev/null | sed -ne 's/GNATMAKE \([^ ]*\).*/\1/p')
GNATMAJORVERSION   = $(word 1,$(subst ., ,$(GNATVERSION)))
HOST_GNATMAJORVERSION = $(word 1,$(subst ., ,$(HOST_GNATVERSION)))
GNATMINORVERSION   = $(word 2,$(subst ., ,$(GNATVERSION)))
GNATPATCHVERSION   = $(word 3,$(subst ., ,$(GNATVERSION)))

GCCDIR_f_clang = $(shell $(callcc) -print-resource-dir)
GCCDIR_f_gcc = $(shell $(callcc) -print-search-dirs | sed -ne 's+^install: \(.*[^/][^/]*\)/+\1+p' )
GCCDIR_f = $(GCCDIR_f_$(BID_COMPILER_TYPE_f))

LDVERSION_f     = $(shell $(callld) -v | $(SED) -e \
		    $(if $(filter LLD,$(shell $(callld) -v)),\
		     '/.* \([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\).*/{s//\1\2\3/;p;q}' -n,\
		     's/.* \([0-9]\)\.\([^. ]*\).*/\1\2/'))
LDNOWARNRWX_f   = $(call checkld,--no-warn-rwx-segments)
GCCSYSLIBDIRS_f = $(shell $(callcc) -print-search-dirs | sed '/^libraries:/{s/^libraries: =\?/-L/;s/:/ -L/g;q;};d')
GCCLIB_file_f   = $(call check_path_absolute,GCCLIB_file_$(1),$(shell $(callcc) -print-file-name=$(1)))
GCCLIB_HOST_f    = $(if $(CONFIG_COMPILER_RT_USE_TOOLCHAIN_LIBGCC),$(call check_path_absolute,GCCLIB_HOST,$(shell $(callcc) -print-libgcc-file-name)))
GCCLIB_EH_HOST_f    = $(if $(CONFIG_COMPILER_RT_USE_TOOLCHAIN_LIBGCC),$(call check_path_absolute,GCCLIB_EH_HOST,$(shell $(callcc) -print-file-name=libgcc_eh.a)))
GCCNOSTACKPROTOPT_f= $(call checkcc,-fno-stack-protector)
GCCSTACKPROTOPT_f = $(call checkcc,-fstack-protector)
GCCSTACKPROTALLOPT_f = $(call checkcc,-fstack-protector-all)
GCCWNONOEXCEPTTYPE_f = $(call checkcxx_nowarn,noexcept-type)
GCCWNOPSABI_f   = $(call checkcxx_nowarn,psabi)
GCCWNOUNUSEDPRIVATEFIELD_f = $(call checkcxx_nowarn,unused-private-field)
GCCWNOUNTERMINATEDSTRINGINITIALIZATION_f = $(call checkcc_nowarn,$\
                                             unterminated-string-initialization)
GCCWNOC99DESIGNATOR_f = $(call checkcxx_nowarn,c99-designator)
GCCARMV5TEFPOPT_arm_f = $(call checkcc,-march=armv5te+fp,-march=armv5te)
GCCARMV6FPOPT_arm_f = $(call checkcc,-march=armv6+fp,-march=armv6)
GCCARMV6T2FPOPT_arm_f = $(call checkcc,-march=armv6t2+fp,-march=armv6t2)
GCCARMV6ZKFPOPT_arm_f = $(call checkcc,-march=armv6zk+fp,-march=armv6zk)
GCCARMV7AFPOPT_arm_f  = $(call checkcc,-march=armv7-a+fp,-march=armv7-a)
GCCARMV7RFPOPT_arm_f = $(call checkcc,-march=armv7-r+fp,-march=armv7-r)
GCCARMV7VEFPOPT_arm_f = $(call checkcc,-march=armv7ve+fp,-march=armv7ve)
GCCARM64OUTLINEATOMICSOPT_arm64_f = $(call checkcc,-mno-outline-atomics)
GCCFORTRANAVAIL_f = $(shell echo | $(callfc) -dD -E - 2>&1 | grep -q __GNUC__ && echo y)
GCCLIBCAVAIL_f  = $(shell echo -e '$(BID_POUND)include <unistd.h>\nint main(void){return 0;}' | $(callcc) -x c -o /dev/null - > /dev/null 2>&1 && echo y)
CLANGVISNEWDELETEHIDDEN_f = $(call checkcxx,-fvisibility-global-new-delete=force-hidden,$\
			      $(call checkcxx,-fvisibility-global-new-delete-hidden))
GCC_HAS_ATOMICS_f = $(shell if echo '$(BID_POUND)include <bits/c++config.h>' | \
		               $(callcxx) -dD -E -x c++ - 2>&1 | \
			       grep -q _GLIBCXX_ATOMIC_BUILTINS; then \
			         echo y; fi)
GCCINCFIXEDPATH_f = $(patsubst %/limits.h,%,$(strip $(firstword $(wildcard \
                      $(addsuffix /limits.h, \
                        $(call GCCDIR_f)/include-fixed$(if $(filter .,$(shell $(callcc) -print-multi-directory)),,/$(shell $(callcc) -print-multi-directory)) \
                        $(call GCCDIR_f)/include-fixed)))))

CONDITIONAL_WARNINGS_MEDIUM_f = $(call checkcc,-Wmissing-prototypes)
CONDITIONAL_WARNINGS_FULL_f   = $(call checkcc,-Wfloat-conversion) \
                                $(call checkcc,-Wfloat-equal) \
                                $(call checkcc,-Wlogical-op)

DIAGNOSTICS_SARIF_f = $(call checkcc,-fdiagnostics-format=sarif -Wno-sarif-format-unstable)
#                      $(call checkcc,-fdiagnostics-format=sarif-stderr)
DIAGNOSTICS_JSON_f  = $(call checkcc,-fdiagnostics-format=json)
DIAGNOSTICS_COLOR_f = $(call checkcc,-fdiagnostics-color=always)

GCCPREFIXOPT_f  = $(call checkcc,-fmacro-prefix-map=$(L4DIR_ABS)/= \
                                 -fmacro-prefix-map=$(OBJ_BASE)/=)

BID_NOSTDINC_clang ?= -nostdinc
BID_NOSTDINC_gcc   ?= -nostdinc
BID_NOSTDINC       ?= $(BID_NOSTDINC_$(BID_COMPILER_TYPE))

# Options that must be filtered from gcc since they are linker flags
BID_GCC_OPTS=-static -shared -nostdlib -Wl$(BID_COMMA)% -L% -l% -PC% -nocrt1 -nocrt -r

# Tool to filter LD flags for the corresponding tool invocation type
#  Convert arguments to a format compatible with gcc invocation
ldflags_to_gcc=$(foreach o,$(1),$(if $(filter $(BID_GCC_OPTS),$o),$o,$(addprefix -Wl$(BID_COMMA),$o)))

ifneq ($(strip $(GCCDIR)),)
GCCINCDIR	= $(GCCDIR)/include $(GCCINCFIXEDPATH)
I_GCCINCDIR	= $(addprefix -isystem ,$(GCCINCDIR))
endif

PKGNAME_DIRNAME := $(notdir $(abspath $(if $(PKGDIR),$(PKGDIR),.)))
ifneq ($(PKGDIR),)
  ifeq ($(origin PKGNAME),undefined)
    PKGNAME := $(PKGNAME_DIRNAME)
  endif
endif

# verbcmd consists of pre-command ($1) main-command ($2) and post-command ($3)
# V=0: nothing is output; V=1: main-command is output; V=2: everything is output
ifeq ($(V),1)
  verbcmd = @echo "$2"; $1 $2 $3
  VERBOSE =
endif
ifeq ($(V),2)
  verbcmd = $1 $2 $3
  VERBOSE =
endif
ifeq ($(V),0)
  verbcmd = @$1 $2 $3
  VERBOSE = @
endif

ifeq ($(D),1)
  DEBUG_MODE = y
endif

ifeq ($(CONFIG_RELEASE_MODE),y)
DEFINES		+= -DL4BID_RELEASE_MODE -DNDEBUG
endif

ifneq ($(filter linux host,$(MODE)),)
HOST_LINK        := 1
HOST_LINK_HOST   := 1
endif

ifneq ($(filter l4linux targetsys,$(MODE)),)
HOST_LINK        := 1
HOST_LINK_TARGET := 1
endif

#
# SUBDIR handling, not within the OBJ-*/ dirs
#
ifeq ($(SYSTEM),)
ifneq ($(SUBDIRS),)
.PHONY: $(SUBDIRS)
$(SUBDIRS):
	$(VERBOSE)$(MAKE) -C $@ all

# we know that SUBDIRS isn't empty, hence we can avoid the dir-test
scrub clean cleanall::
	$(VERBOSE)set -e; $(foreach i,$(SUBDIRS), \
		$(MAKE) -C $(i) $@;)

install:: $(SUBDIRS)
	$(VERBOSE)set -e; $(foreach i,$(SUBDIRS), \
		$(MAKE) -C $(i) $@;)


endif

all:: $(OBJ_DIR)/Makefile

$(OBJ_DIR)/Makefile: $(L4DIR)/mk/Makeconf
	$(call build_obj_redir_Makefile,$@)

else
# we are within an OBJ-*/ dir, create dummy target
$(SUBDIRS):
endif

# Diagnostics
ifeq ($(SUBDIRS_TO_BUILD),)
DIAG_SEARCH_PATHS = $(OBJ_DIR)
else
DIAG_SEARCH_PATHS = $(addprefix $(OBJ_DIR)/,$(SUBDIRS_TO_BUILD))
endif

DIAG_DISPLAY_CMD := cat
diag::
	@printf $(EMPHSTART)"Diagnostics:\n"$(EMPHSTOP)
	@DIAGS=( $$(find $(DIAG_SEARCH_PATHS) -name '*.diag') );  \
	if [[ "$${#DIAGS[@]}" -eq 0 ]] ; then \
	  echo "<None>"; \
	else \
	  $(DIAG_DISPLAY_CMD) "$${DIAGS[@]}"; \
	fi

#
# Dependency section
#
#

# the general dependencies: All generated files depend on ".general.d".
# ".general.d" itself depends on the mk-Makeconf, the optional
# Makeconf.local, the .config.all, the packet-Makeconf.local and the
# Makeconf.local. This ensures a rebuilt if any of the configuration-
# or make-files changes.
#
# We have this nasty if-readable-magic to allow the files to disappear
# or to appear. Depending on if the according makeconf exists now, the
# if-readable magic .general.d is used on existance or non-existence.

BID_DEPEND_GENERAL_D_COND = \
	if [ -r $(1) ] ; then echo -e '$@: $(strip $(1))\n$(strip $(1)):\n' >>$@ ; \
	  else echo '$$(if $$(wildcard $(strip $(1))), $@: FORCE)' >>$@; fi

ifeq ($(SYSTEM),)
GENERAL_D_LOC := $(OBJ_DIR)/.general.d
else
GENERAL_D_LOC := .general.d
endif

$(FIXDEP): $(L4DIR)/tool/kconfig/scripts/basic/fixdep.c
ifeq ($(ROOT_MAKEFILE),1)
	@$(MAKE) genfixdep
else
	@echo \'fixdep\' outdated or unavailable, please call \'make oldconfig\' in the root of your build directory. >&2
	@exit 1
endif

$(dir $(GENERAL_D_LOC)):
	$(VERBOSE)$(MKDIR) $@

$(GENERAL_D_LOC): $(L4DIR)/mk/Makeconf $(FIXDEP) $(EXTRA_GENERAL_D_DEP)
$(GENERAL_D_LOC): | $(dir $(GENERAL_D_LOC))
	@$(BUILD_MESSAGE)
	$(file >$@.in,$(filter-out %.cmd,$(DEPS)))
	$(DEPEND_VERBOSE)cat $@.in | xargs $(RM)
	$(DEPEND_VERBOSE)$(RM) $@.in
	$(DEPEND_VERBOSE)echo '$@: $(SRC_DIR)/Makefile ' > $@
	$(DEPEND_VERBOSE)$(call BID_DEPEND_GENERAL_D_COND,\
		$(OBJ_BASE)/.config.all)
	$(DEPEND_VERBOSE)$(call BID_DEPEND_GENERAL_D_COND,\
		$(OBJ_BASE)/Makeconf.local)
	$(DEPEND_VERBOSE)$(call BID_DEPEND_GENERAL_D_COND,\
		$(OBJ_BASE)/conf/Makeconf.local)
	$(DEPEND_VERBOSE)$(call BID_DEPEND_GENERAL_D_COND,\
		$(L4DIR)/Makeconf.local)
	$(DEPEND_VERBOSE)$(call BID_DEPEND_GENERAL_D_COND,\
		$(L4DIR)/conf/Makeconf.local)
	$(DEPEND_VERBOSE)$(foreach m,$(wildcard $(INCLUDE_MAKE_RULES)),\
	        $(call BID_DEPEND_GENERAL_D_COND,$(m)); )
	$(if $(PKGDIR_ABS),$(DEPEND_VERBOSE)$(call BID_DEPEND_GENERAL_D_COND,\
		$(PKGDIR_ABS)/Makeconf.local))
	$(DEPEND_VERBOSE)$(call BID_DEPEND_GENERAL_D_COND,\
		$(MAKECONFLOCAL))
	$(DEPEND_VERBOSE)$(call BID_DEPEND_GENERAL_D_COND,\
	        $(L4DIR)/mk/arch/Makeconf.$(BUILD_ARCH))

DEPS	+= $(GENERAL_D_LOC)


#
# Messages
#

# coloring on color-capable terminals
# enabled by setting CONFIG_BID_COLORED_PHASES to y
ifeq ($(CONFIG_BID_COLORED_PHASES),y)
  ifneq ($(BID_COLORS_TESTED),y)
    BID_COLORS_TESTED := y
    BID_COLORS_SUPPORTED := $(shell tput colors 2>/dev/null 1>&2; [ $$? -eq 0 ] && echo -n 'y' || echo -n 'n')
    export BID_COLORS_TESTED
    export BID_COLORS_SUPPORTED
  endif
  ifeq ($(BID_COLORS_SUPPORTED), y)
    EMPHSTART = '\033[34;1m'
    EMPHSTOP  = '\033[0m'
  else
    EMPHSTART =
    EMPHSTOP  =
  endif
endif

BID_MESSAGE_TAG            ?= $(PKGDIR_REL)$(if $(filter-out std,$(VARIANT)), - $(VARIANT))

AR_MESSAGE                 ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Archiving into $@"
BUILD_MESSAGE              ?= echo -e "  [$(BID_MESSAGE_TAG)] ... Building $(if $(filter $(GENERAL_D_LOC),$@),Dependencies,$@)"
BUILT_MESSAGE              ?= echo -e $(EMPHSTART)'  [$(BID_MESSAGE_TAG)] ==> $@ built'$(EMPHSTOP)
COMP_MESSAGE               ?= echo -e "  [$(BID_MESSAGE_TAG)] ... Compiling $@$(1)"
COMP_P_MESSAGE             ?= echo -e "  [$(BID_MESSAGE_TAG)] ... Compiling PIC $@"
COMP_PR_MESSAGE            ?= echo -e "  [$(BID_MESSAGE_TAG)] ... Compiling PROFILE $@"
GEN_MESSAGE                ?= echo -e "  [$(BID_MESSAGE_TAG)] ... Generating $(if $(1),$(1),$@)"
LINK_MESSAGE               ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Linking $@"
LINK_SHARED_MESSAGE        ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Linking to shared $@"
LINK_PARTIAL_MESSAGE       ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Partial linking to $@"
DEP_MESSAGE                ?= echo -e "  [$(BID_MESSAGE_TAG)] ... Building dependencies for $<"
CLEAN_MESSAGE              ?= echo -e "  [$(BID_MESSAGE_TAG)] ... Removing created files"
CLEANALL_MESSAGE           ?= echo -e "  [$(BID_MESSAGE_TAG)] ... Removing all created files"
INSTALL_LINK_MESSAGE       ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Updating symlinks"
INSTALL_DOC_MESSAGE        ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Installing $(if $(1),$(1),$(<)) documentation"
INSTALL_DOC_LOCAL_MESSAGE  ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Installing $(if $(1),$(1),$(<)) documentation locally"
INSTALL_MESSAGE            ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Installing $(if $(1),$(1),$^)"
INSTALL_LOCAL_MESSAGE      ?= echo -e "  [$(BID_MESSAGE_TAG)] ==> Installing $(if $(1),$(1),$(<)) to local build-tree"

# allows an include $(DEPSVAR) at the end of the makefile
# but prevents rebuilding them on a scrub, clean, cleanall and help
ifneq ($(filter scrub clean cleanall mrproper help,$(MAKECMDGOALS)),)
DEPSVAR	=
else
DEPSVAR	= $(DEPS)
endif

#
# Some rules
#

# addfileheader-rule: allows "make addfileheader main.c server.c"-like
# commands and automatically inserts the path within the package
# options may be passed with $(ADDFILEHEADER_OPTIONS)
ADDFILEHEADER_PREFIX = $(patsubst $(abspath $(PKGDIR)/)%,\
				  $(PKGNAME)/%,$(abspath ./))
ADDFILEHEADER_FILES = $(filter-out addfileheader,$(MAKECMDGOALS))
addfileheader:
	addfileheader $(ADDFILEHEADER_OPTIONS) -p $(ADDFILEHEADER_PREFIX) $(ADDFILEHEADER_FILES)


.PHONY: FORCE

# 1: name
# 2: output file
# 3: inc path (one only)
# 4: libs
# 5: requires_libs
# 6: PC_CFLAGS
# 7: extras
generate_pcfile =                                                             \
	mkdir -p $(dir $(2));                                                 \
	{ $(if $(3),echo "incdir=/empty_incdir";)                             \
	  echo "Name: $(1)";                                                  \
	  echo "Version: 0";                                                  \
	  echo "Description: L4 library";                                     \
	  $(if $(3)$(6),echo -n "Cflags:";)                                   \
	    $(if $(3),echo -n " $(addprefix -I\$${incdir}/,$(3))";)           \
	    $(if $(6),echo -n " $(6)";)                                       \
	    $(if $(3)$(6),echo;)                                              \
	  $(if $(4),echo "Libs: $(4)";)                                       \
	  $(if $(5),echo "Requires: $(5)";)                                   \
	  $(if $(7),echo -e '$(subst $(newline),\n,$(7))' | sed -e '/^$$/d';) \
	} >$(2);                                                              \
	$(if $(BID_GEN_CONTROL),echo "Provides: $(1)" >> $(PKGDIR)/Control;)  \
	$(if $(BID_GEN_CONTROL),echo "Requires: $(5)" >> $(PKGDIR)/Control;)

define build_obj_redir_Makefile
	$(VERBOSE)install -d $(dir $(1))
	$(VERBOSE)echo '# automatically created -- modifications will be lost' > $(1)
	$(VERBOSE)echo 'SRC := $(if $(2),$(2),$(SRC_DIR))'                    >> $(1)
	$(VERBOSE)echo 'OBJ := $(OBJ_BASE)'                                   >> $(1)
	$(VERBOSE)echo '.PHONY: $$(MAKECMDGOALS) do-all-make-goals'           >> $(1)
	$(VERBOSE)echo 'do-all-make-goals:'                                   >> $(1)
	$(VERBOSE)echo '	@$$(MAKE) -C $$(SRC) O=$$(OBJ) $$(MAKECMDGOALS)'>> $(1)
	$(VERBOSE)echo '$$(MAKECMDGOALS): do-all-make-goals'                  >> $(1)
endef

endif	# _L4DIR_MK_MAKECONF undefined
