##
## General helpers for simplified Makefiles.
##
MAKEFLAGS := -r -R --no-print-directory

targets		:=
deps		:=
deps-after	:=
all-objs	:=
incdeps		:=
_all		:=
_cleanup-y	:=

include scripts/Makefile.rules
include $(obj)/$(makefile)

##
## Append targets to be auto-cleanuped
define add-cleanup-obj-c-by-name
_cleanup-y+= $(1).o
_cleanup-y+= $(1).i
_cleanup-y+= $(1).d
_cleanup-y+= $(1).s
endef

define add-cleanup-obj-S-by-name
_cleanup-y+= $(1).o
_cleanup-y+= $(1).d
_cleanup-y+= $(1).i
endef

##
##
## Generate a bundle of rules for C files
define gen-target-c-bundle
$(eval $(call gen-rule-o-from-c-by-name,$(1),$(2),$(3)))
$(eval $(call gen-rule-i-from-c-by-name,$(1),$(2),$(3)))
$(eval $(call gen-rule-d-from-c-by-name,$(1),$(2),$(3)))
$(eval $(call gen-rule-s-from-c-by-name,$(1),$(2),$(3)))
$(eval $(call add-cleanup-obj-c-by-name,$(1)))
endef

##
##
## Generate a bundle of rules for S files
define gen-target-S-bundle
$(eval $(call gen-rule-o-from-S-by-name,$(1),$(2),$(3)))
$(eval $(call gen-rule-d-from-S-by-name,$(1),$(2),$(3)))
$(eval $(call gen-rule-i-from-S-by-name,$(1),$(2),$(3)))
$(eval $(call add-cleanup-obj-S-by-name,$(1)))
endef

##
##
## Shared or standalone targets
ifneq ($(obj-y),)
obj-y := $(addprefix $(obj)/, $(obj-y))
$(foreach file,						\
	$(obj-y),					\
	$(eval						\
		$(call gen-target-c-bundle,		\
			$(file:.o=),$(file:.o=))))
all-objs += $(obj-y)
deps += $(obj-y:.o=.d)
endif

ifneq ($(obj-x),)
obj-x := $(addprefix $(obj)/, $(obj-x))
obj-x := $(addsuffix $(xsuffix).o, $(obj-x:.o=))
$(foreach file,						\
	$(obj-x),					\
	$(eval						\
		$(call gen-target-c-bundle,		\
			$(file:$(xsuffix).o=),$(file:.o=))))
all-objs += $(obj-x)
deps += $(obj-x:.o=.d)
cleanup-y += $(obj-x) $(obj-x:.o=.d)
endif

ifneq ($(obj-e),)
$(foreach file,						\
	$(obj-e),					\
	$(eval						\
		$(call gen-target-c-bundle,		\
			$(file:.o=),$(file:.o=))))
all-objs += $(obj-e)
deps += $(obj-e:.o=.d)
endif

ifneq ($(asm-y),)
asm-y := $(addprefix $(obj)/, $(asm-y))
$(foreach file,						\
	$(asm-y),					\
	$(eval						\
		$(call gen-target-S-bundle,		\
			$(file:.o=),$(file:.o=))))
all-objs += $(asm-y)
deps += $(asm-y:.o=.d)
endif

ifneq ($(asm-e),)
$(foreach file,						\
	$(asm-e),					\
	$(eval						\
		$(call gen-target-S-bundle,		\
			$(file:.o=),$(file:.o=))))
all-objs += $(asm-e)
deps += $(asm-e:.o=.d)
endif

##
##
## Standalone files where sources are kept in external
## directories. Usually needed when same source files
## are compiled with different flags.
ifneq ($(obj-ext-src-y),)
__obj-ext-src-y := $(addprefix $(obj)/, $(notdir $(obj-ext-src-y)))
$(foreach file,						\
	$(obj-ext-src-y),				\
	$(eval						\
		$(call gen-target-c-bundle,		\
			$(file:.o=),			\
			$(addprefix $(obj)/,$(notdir $(file:.o=))))))
all-objs += $(__obj-ext-src-y)
deps += $(__obj-ext-src-y:.o=.d)
cleanup-y += $(__obj-ext-src-y) $(__obj-ext-src-y:.o=.d)
endif

##
##
## Generate rules for a target
define gen-target-rules

$(1)-all-objs :=

ifneq ($($(1)-obj-y),)
	$(foreach file,						\
		$($(1)-obj-y),					\
		$(eval						\
			$(call gen-target-c-bundle,		\
				$(obj)/$(file:.o=),		\
				$(obj)/$(file:.o=),		\
				$($(1)-obj-y-cflags))))
	$(1)-all-objs += $$(addprefix $(obj)/, $($(1)-obj-y))
	deps += $$(addprefix $(obj)/, $($(1)-obj-y:.o=.d))
endif

ifneq ($($(1)-obj-e),)
	$(foreach file,						\
		$($(1)-obj-e),					\
		$(eval						\
			$(call gen-target-c-bundle,		\
				$(file:.o=),			\
				$(file:.o=),			\
				$($(1)-obj-e-cflags))))
	$(1)-all-objs += $$($(1)-obj-e)
	deps += $$($(1)-obj-e:.o=.d)
endif

ifneq ($($(1)-asm-y),)
	$(foreach file,						\
		$($(1)-asm-y),					\
		$(eval						\
			$(call gen-target-S-bundle,		\
				$(obj)/$(file:.o=),		\
				$(obj)/$(file:.o=),		\
				$($(1)-asm-y-asmflags))))
	$(1)-all-objs += $$(addprefix $(obj)/, $($(1)-asm-y))
	deps += $$($(1)-asm-y:.o=.d)
endif

ifneq ($($(1)-asm-e),)
	$(foreach file,						\
		$($(1)-asm-e),					\
		$(eval						\
			$(call gen-target-S-bundle,		\
				$(file:.o=),			\
				$(file:.o=),			\
				$($(1)-asm-e-asmflags))))
	$(1)-all-objs += $$($(1)-asm-e)
	deps += $$($(1)-asm-e:.o=.d)
endif

$(1)-all-objs += $(all-objs)

$$(obj)/$(1).built-in.o: $$($(1)-all-objs) $$($(1)-libs-e) $(libs-e)
	$$(E) "  LINK    " $$@
	$$(Q) $$(LD) $$(ldflags-y) -r -o $$@ $$^

_all += $$(obj)/$(1).built-in.o
cleanup-y += $$(obj)/$(1).built-in.o
endef

##
##
## Walk over all targets and generate rules they require
$(foreach target,					\
	$(targets),					\
	$(eval						\
		$(call gen-target-rules,$(target))))

##
##
## No targets -- just builtin default one
ifeq ($(targets),)
ifneq ($(all-objs),)
$(obj)/built-in.o: $(all-objs) $(libs-e)
	$(E) "  LINK    " $@
	$(Q) $(LD) $(ldflags-y) -r -o $@ $^

_all += $(obj)/built-in.o
cleanup-y += $(obj)/built-in.o
endif
endif

##
## A rule for building library.
ifneq ($(lib-so),)
$(obj)/$(lib-so).so: $(all-objs) $(libs-e)
	$(E) "  LINK    " $@
	$(Q) $(CC) -shared $(cflags-so) -o $@ $^ $(ldflags-so) $(LDFLAGS)

_all += $(obj)/$(lib-so).so
cleanup-y += $(obj)/$(lib-so).so
endif

##
##
## Include deps if requested
ifneq ($(incdeps),)
ifneq ($(deps-after),)
$(deps): | $(deps-after)
endif
-include $(deps)
endif

##
##
## Autocomplete cleanups
cleanup-y += $(_cleanup-y)

##
## Predefined .PHONY targets
.PHONY: all clean

all: $(_all)
	@true

clean:
	$(E) "  CLEANUP " $(obj)
	$(Q) $(RM) $(cleanup-y)
