TOP_MAKEFILE := $(firstword $(MAKEFILE_LIST))
COMMON_MAKEFILE := $(lastword $(MAKEFILE_LIST))
FARDIR := $(dir $(COMMON_MAKEFILE))

#The following variables can be set by the user:
#
# DEBUG - set if a debug build is needed
# NO_LTO - disable link-time optimisation in release mode
# CLANG - use Clang compiler and LLD linker
# USE_LLD - use LLD linker (useful for debug builds where ld is insanely slow)
# PYTHON - use the python script for language files generation
# LLVM - force the use of llvm tools and libraries
# ARM - (requires CLANG) set to 1 for targeting arm from x86, set to 0 for targeting x86 from arm

# Toolchain setup
TOOLSDIR = $(FARDIR)tools/
FARINCLUDE = $(FARDIR)Include/

DEVNULL = /dev/null

ifneq ($(shell echo %comspec%),%comspec%)
 # Native Windows shell
 os_name = $(subst /,\,$(1))
 UTILS_PREFIX = $(call os_name,$(FARDIR)scripts/gnu.cmd )
 TOOLS_PREFIX = $(TOOLSDIR)
 HOST_TYPE = Windows
 DEVNULL = nul
else
 # Something else
 os_name = $(1)
 HOST_TYPE = $(shell uname -o)
 ifneq (Msys,$(HOST_TYPE))
  ifneq (Cygwin,$(HOST_TYPE))
   HOST_TYPE = Unix
   WINE_CMD = wine #space required
  endif
 else
  TOOLS_PREFIX = $(TOOLSDIR)
  HOST_TYPE = Windows
 endif
endif

LS=$(UTILS_PREFIX)ls
MKDIR = $(UTILS_PREFIX)mkdir -p
RM = $(UTILS_PREFIX)rm -f
CP = $(UTILS_PREFIX)cp -f
MV = $(UTILS_PREFIX)mv -f

ifdef CLANG
CXX = $(GCC_PREFIX)clang --driver-mode=g++
CC = $(GCC_PREFIX)clang
else # CLANG
ifdef ARM
$(error ARM requires CLANG)
endif
CXX = $(GCC_PREFIX)g++$(DW2)
CC = $(GCC_PREFIX)gcc$(DW2)
endif # CLANG

ifdef LLVM
ifndef CLANG
$(error LLVM requires CLANG)
endif
CXX += -stdlib=libc++
TOOL_PREFIX = llvm-
else
TOOL_PREFIX = $(GCC_PREFIX)
endif

WINDRES = $(TOOL_PREFIX)windres
DLLTOOL = $(TOOL_PREFIX)dlltool
RANLIB = $(TOOL_PREFIX)ranlib
AR = $(TOOL_PREFIX)ar
OBJDUMP = $(TOOL_PREFIX)objdump
STRIP = $(TOOL_PREFIX)strip

M4 = $(strip $(call os_name, $(TOOLS_PREFIX)m4)) -P -DFARBIT=$(BITPREFIX)$(DIRBIT) -DBUILD_TYPE=$(FARMANAGER_BUILD_TYPE) -DSCM_REVISION=$(FARMANAGER_SCM_REVISION) -DHOSTTYPE=$(HOST_TYPE)
GAWK = $(strip $(call os_name, $(TOOLS_PREFIX)gawk))

ifdef PYTHON
 ifndef PYTHONBIN
  PYTHONBIN = python
 endif
 LGEN = $(strip $(call os_name, $(PYTHONBIN) $(FARDIR)../misc/lng/lng.generator.py))
else
 LGEN = $(WINE_CMD)$(strip $(call os_name, $(TOOLSDIR)lng.generator.exe))
endif
# Toolchain setup end

# Output directory setup
ifdef DEBUG
 DIRNAME=Debug
else # DEBUG
 DIRNAME=Release
endif # DEBUG

TARGET_MACHINE = $(shell $(CC) -dumpmachine)

ifndef DIRBIT
 ifneq (,$(findstring i686-w64,$(TARGET_MACHINE)))
  DIRBIT = 32
 else
  ifneq (,$(findstring armv7-w64,$(TARGET_MACHINE)))
   DIRBIT = 32
  else
   ifeq (,$(findstring 64,$(TARGET_MACHINE)))
    DIRBIT = 32
   else
    DIRBIT = 64
   endif
  endif
 endif
endif # DIRBIT

ifndef ARM
 ifneq (,$(findstring armv7-w64,$(TARGET_MACHINE)))
  ARM = 1
 else ifneq (,$(findstring aarch64-w64,$(TARGET_MACHINE)))
   ARM = 1
 else
  ARM = 0
 endif
endif # ARM

ifdef ARM
ifneq ($(ARM),0)
BITPREFIX = ARM
endif
endif

ifdef CLANG
DIRSUFFIX=clang
else # CLANG
DIRSUFFIX=gcc
endif # CLANG
# Output directory setup end

# Main flags setup
CFLAGS += \
	-D UNICODE \
	-D _UNICODE \
	-m$(DIRBIT) \
	-fno-common \
	-fdiagnostics-show-option \
	-pipe \
	-funsigned-char \
	-pedantic-errors \
	-Wall \
	-Wextra \
	-Wpedantic \
	-Wfatal-errors \
	-Werror=odr \
	-Werror=return-type \
	-Werror=cast-align=strict \
	-Werror=cast-qual \
	-Werror=implicit-fallthrough=5 \
	-Wdouble-promotion \
	-Wduplicated-branches \
	-Wduplicated-cond \
	-Wformat=2 \
	-Winvalid-pch \
	-Wlogical-op \
	-Wmissing-declarations \
	-Wredundant-decls \
	-Wundef \
	-include $(FARDIR)disabled_warnings.hpp \
	-D NOMINMAX \
	-D WIN32_LEAN_AND_MEAN \
	-D PSAPI_VERSION=1 \

ifeq ($(ARM),0)
CFLAGS += -masm=intel \

endif

CPPFLAGS = $(CFLAGS)\
	-std=c++23 \
	-Werror=old-style-cast \
	-Werror=vexing-parse \
	-Wctor-dtor-privacy \
	-Wextra-semi \
	-Wimplicit-fallthrough \
	-Wnon-virtual-dtor \
	-Woverloaded-virtual \
	-Wsuggest-override \
	-Wzero-as-null-pointer-constant \

LNKFLAGS += \
	-ladvapi32 \
	-lnetapi32 \
	-lmpr \
	-lwinspool \
	-lole32 \
	-loleaut32 \
	-lsecur32 \
	-lsetupapi \
	-lpsapi \
	-lrpcrt4 \
	-luuid \
	-lversion \
	-luserenv \
	-lcomdlg32 \
	-limm32 \
	-m$(DIRBIT) \
	-static \
	-Xlinker --tsaware \
	-Xlinker --dynamicbase \
	-Xlinker --nxcompat \
	-Xlinker --allow-multiple-definition \

# Configuration-specific flags
ifdef DEBUG
# Debug mode
CFLAGS += \
	-D_DEBUG \
	-D_GLIBCXX_ASSERTIONS \
	-g \
	-Wa,-mbig-obj \
#	-D_GLIBCXX_DEBUG \
#	-D_GLIBCXX_DEBUG_PEDANTIC \

else # DEBUG
# Release mode
CFLAGS += \
	-DNDEBUG \
	-O3 \

ifneq ($(NO_LTO),1)
CFLAGS += -flto

ifndef CLANG
CFLAGS += \
	-flto-odr-type-merging \

endif

endif # NO_LTO

LNKFLAGS += \
	-Xlinker --gc-sections \

endif # DEBUG
# Configuration-specific flags end

# Compiler-specific flags
ifdef CLANG
START_NO_UNUSED_ARGS=--start-no-unused-arguments
END_NO_UNUSED_ARGS=--end-no-unused-arguments
endif

PDB_FLAGS = \
	-g \
	$(START_NO_UNUSED_ARGS) \
	-gcodeview \
	$(END_NO_UNUSED_ARGS) \

LLD_FLAGS = \
	-fuse-ld=lld \
	-Xlinker -Map \
	-Xlinker $(MAP) \
	-Xlinker --pdb= \

ifdef CLANG
ifeq ($(DIRBIT),32)
ifeq ($(ARM),0)
CLANG_TARGET = i686-w64-windows-gnu
else
CLANG_TARGET = armv7-w64-windows-gnu
endif
else
ifeq ($(ARM),0)
CLANG_TARGET = x86_64-w64-windows-gnu
else
CLANG_TARGET = aarch64-w64-windows-gnu
endif
endif

CLANG_FLAGS = \
	-target $(CLANG_TARGET) \
	-Wno-unknown-warning-option \
	-fms-extensions \
	-fno-emulated-tls \
	-Weverything \
	$(PDB_FLAGS) \

ifdef LLVM
CLANG_FLAGS += \
	--start-no-unused-arguments \
	-rtlib=compiler-rt \
	-unwindlib=libunwind \
	--end-no-unused-arguments \

endif

CFLAGS += $(CLANG_FLAGS)

LNKFLAGS += \
	$(CLANG_FLAGS) \
	$(LLD_FLAGS) \

else

CFLAGS += \
	-flarge-source-files \

ifndef USE_LLD
# LD map files are unusable for tracing,
# so we keep the symbols, objdump them and strip manually.
# LLD, on the contrary, generates good maps and doesn't need this trickery.
USE_OBJDUMP_MAPS=1
endif

endif
# Compiler-specific flags end

# Platform-specific flags
ifeq ($(DIRBIT),32)
LNKFLAGS += \
	-Xlinker --large-address-aware \

ifeq ($(ARM),0)
WINDRES += -F pe-i386 \

endif
else
ifeq ($(ARM),0)
WINDRES += -F pe-x86-64 \

endif
endif

ifneq ($(ARM),0)
WINDRES += -F $(CLANG_TARGET) \

endif
# Platform-specific flags end

ifndef USE_OBJDUMP_MAPS
ifdef DEBUG
LNKFLAGS += \
	-Xlinker --strip-all \

endif
endif

ifdef USE_LLD
CFLAGS += $(PDB_FLAGS)
LNKFLAGS += $(LLD_FLAGS)
endif

ifeq ($(findstring ----,---$(strip $(MAKEFLAGS))-),)
 MK_FLAGS := -$(strip $(MAKEFLAGS))
else
 MK_FLAGS := $(strip $(MAKEFLAGS))
endif
