# vi:set ft=make:
# -*- Makefile -*-
# Fiasco make configuration file
#
# created 1996/04/20 by hohmuth
#

# ccache would make feature detection slower
CHECKCC  = $(if $(shell $(filter-out $(CCACHE),$(CC)) $(1) $(CHECKCC_FLAGS) \
           -c -o /dev/null -xc /dev/null >/dev/null 2>&1 || echo X),$(2),$(1))
CHECKCXX = $(if $(shell $(filter-out $(CCACHE),$(CXX)) $(1) $(CHECKCXX_FLAGS) \
           -c -o /dev/null -xc++ /dev/null >/dev/null 2>&1 || echo X),$(2),$(1))
CHECKLD  = $(shell if $(LD) -v "$1" > /dev/null 2>&1; $\
           then echo "$(1)"; else echo "$(2)"; fi)

eval_impl = $(if $($(1)_IMPL),$($(1)_IMPL),$(1))

include $(objbase)/globalconfig.out

# use patsubst here to prevent confusion of syntax highlighting editors :-)
CONFIG_XARCH	:= $(patsubst "%",%,$(CONFIG_XARCH))
CONFIG_ABI	:= $(patsubst "%",%,$(CONFIG_ABI))

SYSTEM		:= $(shell uname)
CC		:= $(patsubst "%",%,$(CONFIG_CC))
CXX		:= $(patsubst "%",%,$(CONFIG_CXX))
LD		:= $(patsubst "%",%,$(CONFIG_LD))
HOST_CC		:= $(patsubst "%",%,$(CONFIG_HOST_CC))
HOST_CXX	:= $(patsubst "%",%,$(CONFIG_HOST_CXX))
RM		:= rm -f
RM_R		:= rm -rf
ECHO		:= echo
ECHO_E		:= bash --norc -c 'echo -e "$$0" "$$@"'
CP		:= cp
GREP		:= GREP_OPTIONS= grep
AWKP		?= gawk --posix
CCACHE		?= ccache
CFLAGS		:=
CXXFLAGS	:=

OPT_CFLAGS	:= $(if $(CONFIG_OPTIMIZE_FOR_SIZE),-Os,-O3)
OPT_CXXFLAGS	:= $(if $(CONFIG_OPTIMIZE_FOR_SIZE),-Os,-O3)
NOOPT_CFLAGS	:= -Os
NOOPT_CXXFLAGS	:= -Os
ARFLAGS		:= crsU
srcdir		?= .
L4DIR		?= $(srcdir)/../../../l4
tooldir		:= $(srcdir)/../tool
MOVE_IF_CHANGE	:= $(srcdir)/../tool/move-if-change
SHOWDEPS	:= $(srcdir)/../tool/showdeps
PREPROCESSDEPS	:= $(srcdir)/../tool/parsedeps
CIRCULAR	:= $(srcdir)/../tool/circular

ifeq ($(SYSTEM),FreeBSD)
  MD5SUM	:= /sbin/md5
else
  MD5SUM	:= md5sum
endif

# The name of this file can be overridden from the command line or in
# objdir/Makeconf.local.
MODULES_FILE	= $(srcdir)/Modules.$(CONFIG_XARCH)

PREPROCESS = $(srcdir)/../tool/preprocess/src/preprocess

ifeq ($(CONFIG_INLINE),y)
  PREPROCESS_FLAGS = -i -t
else
  PREPROCESS_FLAGS = -t
endif

AS      = $(CROSS_COMPILE)as
AR      = $(CROSS_COMPILE)ar
NM      = $(CROSS_COMPILE)nm
RANLIB  = $(CROSS_COMPILE)ranlib
SIZE    = $(CROSS_COMPILE)size
STRIP   = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump

# Include architecture-specific rules. These may overwrite anything above
include $(srcdir)/Makeconf.$(CONFIG_XARCH)

# Include user-specific rules. These may overwrite anything above
-include $(srcdir)/Makeconf.local
-include $(objbase)/Makeconf.local

get_cc_version_part = $\
  $(shell echo $(1) | $(filter-out $(CCACHE),$(CC)) -E -xc - | tail -1)

ifneq ($(CCXX_VERSION),)
  CCXX_SUFFIX := -$(CCXX_VERSION)
endif

# We would use "CPP ?= cpp" but make has a default of "$(CC) -E" so we need to
# overwrite the default with 'cpp' too, because 'gcc -E' does not always work
# correctly.
CPP := $(if $(filter undefined default,$(origin CPP)),cpp,$(CPP))
CPP := $(CROSS_COMPILE)$(CPP)$(CCXX_SUFFIX)

detect_cross = $(if $(shell $\
	         sh -c "command -v $(CROSS_COMPILE)$(1)"),$(CROSS_COMPILE),)$(1)
CC_TYPE      := $(if $(findstring clang,$\
                 $(shell $(call detect_cross,$(CC)) --version)),clang,gcc)
CC_IS_CLANG  := $(if $(filter clang,$(CC_TYPE)),1)
CXX_TYPE     := $(if $(findstring clang,$\
                  $(shell $(call detect_cross,$(CXX)) --version)),clang,gcc)
CXX_IS_CLANG := $(if $(filter clang,$(CXX_TYPE)),1)
LD_TYPE      := $(if $(and $(LD),$(findstring LLD,$\
                  $(shell $(call detect_cross,$(LD)) --version))),clang,gnu)
CLANG_TARGET_FLAG = $(addprefix --target=, $(notdir $(CROSS_COMPILE:%-=%)))

comp_prefixes = $(CCXX_WRAP) $(if $(USE_CCACHE),$(CCACHE))
ifeq ($(CC_TYPE),clang)
  CC := $(call comp_prefixes) $(CC)$(CCXX_SUFFIX) $(CLANG_TARGET_FLAG)
  CHECKCC_FLAGS := -Werror=unknown-warning-option
else
  CC := $(call comp_prefixes) $(CROSS_COMPILE)$(CC)$(CCXX_SUFFIX)
endif

ifeq ($(CXX_TYPE),clang)
  CXX := $(call comp_prefixes) $(CXX)$(CCXX_SUFFIX) $(CLANG_TARGET_FLAG)
  CHECKCXX_FLAGS := -Werror=unknown-warning-option
else
  CXX := $(call comp_prefixes) $(CROSS_COMPILE)$(CXX)$(CCXX_SUFFIX)
endif

ifneq ($(LD_TYPE),clang)
  LD := $(CROSS_COMPILE)$(LD)
endif

CCVER_ID_MAJOR-gcc        := __GNUC__
CCVER_ID_MINOR-gcc        := __GNUC_MINOR__
CCVER_ID_PATCHLEVEL-gcc   := __GNUC_PATCHLEVEL__
CCVER_ID_MAJOR-clang      := __clang_major__
CCVER_ID_MINOR-clang      := __clang_minor__
CCVER_ID_PATCHLEVEL-clang := __clang_patchlevel__

CCVER_MAJOR         := $(call get_cc_version_part, $(CCVER_ID_MAJOR-$(CC_TYPE)))
CCVER_MINOR         := $(call get_cc_version_part, $(CCVER_ID_MINOR-$(CC_TYPE)))
CCVER_PATCHLEVEL    := $(call get_cc_version_part, $(CCVER_ID_PATCHLEVEL-$(CC_TYPE)))
CCVER_VERSION-gcc   := $(CCVER_MAJOR)
CCVER_VERSION-clang := $(CCVER_MAJOR)$(if $(filter $(CCVER_MAJOR),3),.$(CCVER_MINOR))
CCVER_VERSION       := $(CCVER_VERSION-$(CC_TYPE))
CCVER_RELEASE-gcc   := $(CCVER_MAJOR).$(CCVER_MINOR)$(if $(filter $\
                         $(CCVER_MAJOR),1 2 3 4),.$(CCVER_PATCHLEVEL))
CCVER_RELEASE-clang := $(CCVER_MAJOR).$(CCVER_MINOR).$(CCVER_PATCHLEVEL)
CCVER_RELEASE       := $(CCVER_RELEASE-$(CC_TYPE))
CCVER_FULL          := $(CCVER_MAJOR).$(CCVER_MINOR).$(CCVER_PATCHLEVEL)


L4ALL_INCDIR        ?= $(addprefix -I, $(PRIVATE_INCDIR))
L4STD_INCDIR        ?= -nostdinc
L4STD_INCDIR_LAST   ?= -isystem $(GCCINCDIR)
KERNEL_LDFLAGS      += -gc-sections
SHARED_FLAGS        += -fno-pic
SHARED_FLAGS        += -ffunction-sections -fdata-sections
SHARED_FLAGS-gcc    += -fno-defer-pop
SHARED_FLAGS-gcc    += $(if $(CONFIG_FULL_PATH_NAMES_IN_BINARIES),,$\
                       $(call CHECKCC,-fmacro-prefix-map=$(abspath $(srcdir)/../)=.))
# gcc #57845
SHARED_FLAGS-gcc    += $(if $(filter sparc,$(CONFIG_XARCH)),,-freg-struct-return)
SHARED_FLAGS-gcc    += $(call CHECKCXX,-Wmisleading-indentation,) $\
                       -Wlogical-op -Wfloat-equal
SHARED_FLAGS-gcc    += $(if $(filter 7 8 9 10,$(CCVER_MAJOR)),-Wno-nonnull)
SHARED_FLAGS        += $(if $(CHECK_CI_BUILDCHECK),,-g)
SHARED_FLAGS        += -Wall -Wextra $(call CHECKCC,-Wdouble-promotion) $\
                       $(call CHECKCC,-Wfloat-conversion) -Wmissing-declarations
SHARED_FLAGS        += -Wdeprecated
SHARED_FLAGS        += -Wimplicit-fallthrough
SHARED_FLAGS-clang  += -Wunreachable-code-aggressive

# WARNING EXCEPTION: not all members of a struct used to reflect hardware state
# are necessarily used.
SHARED_FLAGS-clang  += -Wno-unused-private-field
# WARNING EXCEPTION: warnings about hiding overloaded functions do not expose
# problems in this code
SHARED_FLAGS-clang  += -Wno-overloaded-virtual
# WARNING EXCEPTION: checking a parameter marked with the nonnull attribute for
# NULL is defensive programming.
SHARED_FLAGS-gcc    += $(if $(call CHECKCC,-Wnonnull-compare),-Wno-nonnull-compare)
# WARNING EXCEPTION: checked during code review
SHARED_FLAGS-gcc    += $(if $(call CHECKCC,-Waddress-of-packed-member),$\
                                           -Wno-address-of-packed-member)
SHARED_FLAGS        += -Wundef

SHARED_FLAGS        += $(call CHECKCC,-Wformat=2,)
SHARED_FLAGS        += $(call CHECKCC,-fno-stack-protector,)
SHARED_FLAGS        += $(call CHECKCC,-fdiagnostics-show-option)
SHARED_FLAGS        += $(call CHECKCC,-fno-builtin-bcmp)

OPT_CXXFLAGS-gcc    += $(call CHECKCXX,-fweb,)
OPT_CFLAGS-gcc      += $(call CHECKCC,-fweb,)
OPT_SHARED_FLAGS-gcc += $(call CHECKCC,-frename-registers,)
OPT_SHARED_FLAGS-gcc += $(call CHECKCC,-fgcse-after-reload,)
CXXFLAGS-gcc        += -fno-implement-inlines
CXXFLAGS            += -std=c++20 $(call CHECKCXX,-fno-sized-deallocation,)
CXXFLAGS            += $(call CHECKCXX,-Wsuggest-override)

LDFLAGS             += $(call CHECKLD,--no-warn-rwx-segments,) $\
                       $(call CHECKLD,--no-warn-execstack,)

WNO_DEPRECATED_NON_PROTOTYPE := $(if $(call CHECKCC,-Wdeprecated-non-prototype),$\
                                                    -Wno-deprecated-non-prototype)
DIAGNOSTICS_COLOR := $(call CHECKCC,-fdiagnostics-color=always)


# There are older versions of clang that have the mcdc flag but the feature is
# not at the stage where we want to use it. That's why we explicitly check for
# the version number here and dont use CHECK_CXX.
CLANG_SUPPORTS_MCDC    := $(shell [ $(CCVER_VERSION) -gt 17 ] && echo 1)

ifneq ($(CONFIG_COV),)
  SHARED_FLAGS-gcc   += -fprofile-arcs -ftest-coverage
  SHARED_FLAGS-clang += -fprofile-instr-generate -fcoverage-mapping  \
                        $(if $(CLANG_SUPPORTS_MCDC),-fcoverage-mcdc) \
                        -mllvm -instrprof-atomic-counter-update-all
  KERNEL_LDFLAGS     += $(KERNEL_LDFLAGS-$(CC_TYPE))
  OBJ_KERNEL-clang   += fiasco-covpath.o
  OBJ_KERNEL         += $(OBJ_KERNEL-$(CC_TYPE))
  OPT_CFLAGS-gcc     += -O1
  OPT_CXXFLAGS-gcc   += -O1
endif

-include $(objbase)/.Host-config

# Configuration dependent compile flags
SHARED_FLAGS-$(CONFIG_NDEBUG)           += -DNDEBUG
SHARED_FLAGS += $(if $(CONFIG_NO_FRAME_PTR),-f,-fno-)omit-frame-pointer

# To enable all warnings, including those with false positives.
ENABLE_ALL_WARNINGS ?= n
ifeq ($(ENABLE_ALL_WARNINGS),y)
  SHARED_FLAGS += $(call CHECKCC,-Wframe-larger-than=512)
  SHARED_FLAGS += -Wconversion -Wsign-conversion
else
  # WARNING EXCEPTION: the variables are initialized when the warned about code
  # is reached. The false positives are only produced by GCC on -Os
  # optimization level.
  SHARED_FLAGS-gcc += $(call CHECKCC,-Wno-maybe-uninitialized)
endif

# Eagerly compute SHARED_FLAGS to avoid calling CHECKCC over and over again.
SHARED_FLAGS       := $(SHARED_FLAGS) $(SHARED_FLAGS-y)
SHARED_FLAGS       += $(SHARED_FLAGS-$(CC_TYPE))
OPT_SHARED_FLAGS   += $(OPT_SHARED_FLAGS-$(CC_TYPE))
NOOPT_SHARED_FLAGS += $(NOOPT_SHARED_FLAGS-$(CC_TYPE))

# Eagerly compute LDFLAGS to avoid calling CHECKLD over and over again.
LDFLAGS         := $(LDFLAGS)

# Standard compile flags
ASFLAGS		+= $(SHARED_FLAGS) -DASSEMBLER
ASFLAGS-clang	+= -no-integrated-as
CFLAGS		+= $(SHARED_FLAGS) -Wbad-function-cast -Wstrict-prototypes $\
                   -Wmissing-prototypes
CXXFLAGS	+= $(SHARED_FLAGS) -fno-rtti -fno-exceptions -Wold-style-cast
OPT_CFLAGS	+= $(OPT_SHARED_FLAGS)
OPT_CXXFLAGS	+= $(OPT_SHARED_FLAGS)
NOOPT_CFLAGS	+= $(NOOPT_SHARED_FLAGS)
NOOPT_CXXFLAGS	+= $(NOOPT_SHARED_FLAGS)
CPPFLAGS	+= $(L4STD_INCDIR) $(L4ALL_INCDIR) $(L4STD_INCDIR_LAST)

CXXFLAGS	+= $(call CHECKCXX,-fno-threadsafe-statics,)

ASFLAGS         += $(ASFLAGS-$(CC_TYPE))
CPPFLAGS        += $(CPPFLAGS-$(CC_TYPE))
CXXFLAGS        += $(CXXFLAGS-$(CC_TYPE))
OPT_CFLAGS      += $(OPT_CFLAGS-$(CC_TYPE))
OPT_CXXFLAGS    += $(OPT_CXXFLAGS-$(CC_TYPE))
NOOPT_CFLAGS    += $(NOOPT_CFLAGS-$(CC_TYPE))
NOOPT_CXXFLAGS  += $(NOOPT_CXXFLAGS-$(CC_TYPE))

HOST_CPPFLAGS   +=
HOST_CFLAGS     += -O2 -Wall -Wextra
HOST_CXXFLAGS   += -O2 -Wall -Wextra

# for stddef.h, ...
GCCINCDIR       := $(shell $(filter-out $(CCACHE),$(CXX)) $(SHARED_FLAGS) $\
                   -print-file-name=include)


# Output formatting:
# - set V=0 to prevent printing them (default)
# - set V=1 to see the command line of all tools except the preprocessor
# - set V=2 to see the command line of all tools including the preprocessor
#           and verbose output from `move-if-change`
VERBOSE         ?= @
VERBOSE_PREP    ?= @
ifeq ($(V),1)
  VERBOSE :=
else ifeq ($(V),2)
  VERBOSE :=
  VERBOSE_PREP :=
  PREP_MESSAGE  ?= @$(ECHO) "  ... Preprocessing $(filter %.cpp,$^)"
endif
AR_MESSAGE	?= @$(ECHO) "  ==> Archiving into $@"
COMP_MESSAGE	?= @$(ECHO) "  ... Making $@"
LINK_MESSAGE	?= @$(ECHO) "  ==> Linking $@"
DEP_MESSAGE	?= @$(ECHO) "  ... Building dependencies for $<"
PREP_MESSAGE	?= @$(ECHO) "  ... Preprocessing $*"
PATCH_MESSAGE   ?= @$(ECHO) "  ... Writing checksum into "
STRIP_MESSAGE	?= @$(ECHO) "  ... Stripping $@"
GENVER_MESSAGE	?= @$(ECHO) "  ... Generating version information" 
CLEAN_MESSAGE	?= @$(ECHO) "Removing created files"
CLEANALL_MESSAGE?= @$(ECHO) "Removing all created files"
INST_MESSAGE	?= @$(ECHO) "Installing $(^F)"
OK_MESSAGE      ?= 

COMP_MESSAGE_NOOPT ?= $(COMP_MESSAGE)

ADD_CONFIGFILE   = $\
  $(VERBOSE)$(OBJCOPY) --add-section .debug.fiasco_config=globalconfig.out $(1)

# Do not use built-in rules and variables for a cleaner environment.
# Use the latest mtime between symlinks and target.
MAKEFLAGS += -rRL

ifneq ($(V),2)
MOVE_IF_CHANGE += -q
endif

SRC_ALL = $(SRC_S) $(SRC_CPP) $(SRC_C) $(SRC_CC)
ifeq ($(filter clean cleanall mrproper,$(MAKECMDGOALS)),)
DEPS	= $(foreach file,$(SRC_ALL), $(dir $(file)).$(notdir $(file)).d)

CC_WHITELIST-gcc     := 11 12 13 14 15
# Clang >= 5 required to support C++17. Furthermore:
#  - ARM: Clang<9:  "asm goto" constructs not supported
#  - IA32: Clang<9: "CC_HAS_ASM_FLAG_OUTPUTS" not supported
CC_WHITELIST-clang   := 10 11 12 13 14 15 16 17 18 19 20 21
CC_WHITELIST         := $(CC_WHITELIST-$(CC_TYPE))

# https://github.com/llvm/llvm-project/issues/53645 -- e.g. printf=>puts
CC_BLACKLIST-ia32-clang := 12 13 14

# CC_LAST_BROKEN-REVISION* specifies the last broken minor version
# For gcc these are minor version numbers, for clang it is the patchlevel.
CC_LAST_BROKEN_REVISION-gcc-11 := 1

CC_LAST_BROKEN_REVISION := $(CC_LAST_BROKEN_REVISION-$(CC_TYPE)-$(CCVER_MAJOR))

ifneq ($(DISABLE_CC_CHECK),y)
ifeq ($(filter $(CC_WHITELIST),$(CCVER_VERSION)),)
  $(error $(CC_TYPE)-$(CCVER_VERSION) is not supported. \
          Please use one of the following versions: $(CC_WHITELIST))
endif

CC_BUGFIX_TYPE-clang = PATCHLEVEL
CC_BUGFIX_TYPE-gcc = MINOR

# This relies on minor versions being single digit!
ifneq ($(CC_LAST_BROKEN_REVISION),)
  CC_BUGFIX_VERSION := $(CCVER_$(CC_BUGFIX_TYPE-$(CC_TYPE)))
  MIN_VER := $(word 2,$(sort $(CC_LAST_BROKEN_REVISION) $(CC_BUGFIX_VERSION)))
  ifeq ($(CC_LAST_BROKEN_REVISION),$(or $(MIN_VER),$(CC_LAST_BROKEN_REVISION)))
    $(error $(CC_TYPE)-$(CCVER_FULL) is blacklisted, please update)
  endif
endif

ifneq ($(strip \
	$(filter $(CC_BLACKLIST-$(CONFIG_XARCH)-$(CC_TYPE)),$(CCVER_RELEASE)) \
	$(filter $(CC_BLACKLIST-$(CC_TYPE)),$(CCVER_RELEASE)) \
	$(filter $(CC_BLACKLIST-$(CONFIG_XARCH)-$(CC_TYPE)),$(CCVER_VERSION)) \
	$(filter $(CC_BLACKLIST-$(CC_TYPE)),$(CCVER_VERSION))),)
  $(error $(CC_TYPE)-$(CCVER_FULL) is blacklisted, please update)
endif
endif

else
DEPS    = /dev/null
endif
