CLANG_C_FLAGS = -Weverything -Wno-vla -Wno-covered-switch-default -Werror
GCC_C_FLAGS = -Wall -Wextra -Werror

ifeq ($(notdir $(CC)), clang)
  CFLAGS ?= $(CLANG_C_FLAGS)
else ifeq ($(notdir $(CC)), suncc)
  CFLAGS ?= -errwarn -errtags
  C_OUTPUT_FILE := -o
else ifeq ($(notdir $(CC)),$(filter $(notdir $(CC)),armcl cl2000 cl430 cl6x cl7x clpru))
  CFLAGS ?= --display_error_number --emit_warnings_as_errors --issue_remarks -i $(dir $(shell readlink -f $(shell which $(CC))))../include
  C_OUTPUT_FILE := --output_file=
else ifeq ($(notdir $(CC)), pgcc)
  CFLAGS ?= -Minform=inform -c11
  C_OUTPUT_FILE := -o
else ifeq ($(notdir $(CC)), pgc++)
  CFLAGS ?= --display_error_number -Minform=inform -Werror
  C_OUTPUT_FILE := -o
else ifeq ($(notdir $(CC)), xlc)
  CFLAGS ?= $(CLANG_C_FLAGS)
  C_OUTPUT_FILE := -o
else ifeq ($(notdir $(CC)), icc)
  CFLAGS ?= -w3 -Werror
  C_OUTPUT_FILE := -o
else ifeq ($(notdir $(CC)), tcc)
  CFLAGS ?=
  C_OUTPUT_FILE := -o
else ifeq ($(notdir $(CC)), gcc)
  CFLAGS ?= $(GCC_C_FLAGS)
  C_OUTPUT_FILE := -o
else ifeq ($(notdir $(CC)), x86_64-w64-mingw32-gcc)
  CFLAGS ?= $(GCC_C_FLAGS)
  C_OUTPUT_FILE := -o
else ifeq ($(notdir $(CC)), ccomp)
  CFLAGS ?= -Wall
  C_OUTPUT_FILE := -o
else
  CFLAGS ?= $(filter-out -Werror,$(GCC_C_FLAGS))
endif

CLANG_CXX_FLAGS = -Weverything -Werror
GCC_CXX_FLAGS = -Wall -Wextra -Werror

ifeq ($(notdir $(CXX)), clang++)
  CXXFLAGS ?= $(CLANG_CXX_FLAGS)
else ifeq ($(notdir $(CXX)), sunCC)
  CXXFLAGS ?= -errwarn=%all -errtags=yes
  CXX_OUTPUT_FILE := -o
else ifeq ($(notdir $(CXX)), pgc++)
  CXXFLAGS ?= --display_error_number -Minform=inform -Werror
  CXX_OUTPUT_FILE := -o
else ifeq ($(notdir $(CXX)),$(filter $(notdir $(CC)),armcl cl2000 cl430 cl6x cl7x clpru))
  CXXFLAGS ?= --display_error_number --emit_warnings_as_errors --issue_remarks -i $(dir $(shell readlink -f $(shell which $(CC))))../include
  CXX_OUTPUT_FILE := --output_file=
else ifeq ($(notdir $(CXX)), xlc++)
  CXXFLAGS ?= $(CLANG_CXX_FLAGS)
  CXX_OUTPUT_FILE := -o
else ifeq ($(notdir $(CXX)), icpc)
  CXXFLAGS ?= $(GCC_CXX_FLAGS)
  CXX_OUTPUT_FILE := -o
else ifeq ($(notdir $(CXX)), g++)
  CXXFLAGS ?= $(GCC_CXX_FLAGS)
  CXX_OUTPUT_FILE := -o
else ifeq ($(notdir $(CXX)), x86_64-w64-mingw32-g++)
  CFLAGS ?= $(GCC_CXX_FLAGS)
  C_OUTPUT_FILE := -o
else
  CXXFLAGS ?= $(filter-out -Werror,$(GCC_CXX_FLAGS))
endif

C_OUTPUT_FILE ?= --output=
CXX_OUTPUT_FILE ?= --output=

NULL =

TESTS = \
	array-param \
	assume \
	cast \
	concat \
	const \
	constant \
	deprecated \
	empty-bases \
	fallthrough \
	flags \
	inline \
	likely \
	malloc \
	message \
	noinline \
	no-escape \
	no-return \
	non-null \
	predict \
	printf-format \
	pure \
	require \
	restrict \
	returns-non-null \
	sentinel \
	static-assert \
	stringify \
	unknown-cpp-attributes \
	unknown-pragmas \
	unavailable \
	unreachable \
	unused-function \
	visibility \
	warn \
	warn-unused-result \
	$(NULL)

ifneq ($(CXX), none)
CXXTESTS = $(TESTS:=-cpp)
else
CXXTESTS =
endif

CLEANFILES = \
	$(TESTS) \
	$(TESTS:=-cpp) \
	$(TESTS:=.cpp)

all: $(TESTS) $(CXXTESTS) .gitignore Makefile.msvc Makefile.iar Makefile.dmc

%.cpp: %.c
	cp -a $^ $@

%-cpp: %.cpp
	$(CXX) $(CXXFLAGS) $(CXX_OUTPUT_FILE)$@ $(^:.c=.cpp)

%: %.c
	$(CC) $(CFLAGS) $(C_OUTPUT_FILE)$@ $^

define GEN_GCC_C_WARNINGS
$(1): $(1:=.c)
	$(CC) $(filter-out -Werror,$(CFLAGS)) $(C_OUTPUT_FILE)$(1) $(1:=.c)
endef

define GEN_GCC_CXX_WARNINGS
$(1)-cpp: $(1:=.cpp)
	$(CXX) $(filter-out -Werror,$(CXXFLAGS)) $(CXX_OUTPUT_FILE)$(1) $(1:=.cpp)
endef

.gitignore: Makefile
	@echo "# Generated by Makefile. Do not edit." > $@
	@$(foreach file,$(TESTS),echo -e "/$(file)\n/$(file:=.exe)\n/$(file:=.cpp)\n/$(file:=-cpp)\n/$(file:=-cpp.exe)\n/$(file:=-cpp.o)\n/$(file:=.obj)\n/$(file:=.lib)\n/$(file:=-cpp.obj)\n/$(file:=-cpp.lib)\n/$(file:=.o)\n/$(file:=.map)\n/$(file:=-cpp.map)" >> $@;)
	@echo "/.gitignore" >> $@

Makefile.iar: Makefile
	@echo -e "# Generated by Makefile. Do not edit.\r" > $@
	@echo -e "CC = \"C:\x5cProgram Files (x86)\x5cIAR Systems\x5cEmbedded Workbench 8.4\x5carm\x5cbin\x5ciccarm.exe\"\r" >> $@
	@echo -e "CXX = \x24(CC)\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "CFLAGS = --silent --remarks --warnings_are_errors\r" >> $@
	@echo -e "CXXFLAGS = --c++ \x24(CFLAGS)\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "TESTS = \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.o) $(file:=-cpp.o) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "CLEANFILES = \x24(TESTS) \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.cpp) $(file:=.o) $(file:=-cpp.o) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "all: \x24(TESTS)\r\n\r" >> $@
	@echo -e "clean:\r\n\tdel /Q \x24(CLEANFILES)\r\n\r" >> $@

	@$(foreach file,$(TESTS),echo -e "$(file:=.o): $(file:=.c)\r\n\t\x24(CC) \x24(CFLAGS) --output \x24(@) \x24(?)\r\n$(file:=.cpp): $(file:=.c)\r\n\tcopy /Y \x24(?) \x24(@) >NUL\r\n$(file:=-cpp.o): $(file:=.cpp)\r\n\t\x24(CXX) \x24(CXXFLAGS) --output \x24(@) \x24(?)\r\n\r" >> $@;)

Makefile.dmc: Makefile
	@echo -e "# Generated by Makefile. Do not edit.\r" > $@
	@echo -e "CC = \"dmc.exe\"\r" >> $@
	@echo -e "CXX = \x24(CC)\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "CFLAGS = -w -w17 -w18 -wx\r" >> $@
	@echo -e "CXXFLAGS = -wc \x24(CFLAGS)\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "TESTS = \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.exe) $(file:=-cpp.exe) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "CLEANFILES = \x24(TESTS) \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.cpp) $(file:=.exe) $(file:=-cpp.exe) $(file:=.map) $(file:=-cpp.map) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "all: \x24(TESTS)\r\n\r" >> $@
	@echo -e "clean:\r\n\tdel /Q \x24(CLEANFILES)\r\n\r" >> $@

	@$(foreach file,$(TESTS),echo -e "$(file:=.exe): $(file:=.c)\r\n\t\x24(CC) \x24(CFLAGS) -o \x24(@) \x24(?)\r\n$(file:=.cpp): $(file:=.c)\r\n\tcopy /Y \x24(?) \x24(@) >NUL\r\n$(file:=-cpp.exe): $(file:=.cpp)\r\n\t\x24(CXX) \x24(CXXFLAGS) -o \x24(@) \x24(?)\r\n\r" >> $@;)

Makefile.msvc: Makefile
	@echo -e "# Generated by Makefile. Do not edit.\r" > $@
	@echo -e "CC = cl\r" >> $@
	@echo -e "CXX = \x24(CC)\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "CFLAGS = /nologo /Wall /wd4464 /wd4514\r" >> $@
	@echo -e "CXXFLAGS = \x24(CFLAGS)\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "TESTS = \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.exe) $(file:=-cpp.exe) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "TESTS = \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.exe) $(file:=-cpp.exe) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "CLEANFILES = \x24(TESTS) \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.cpp) $(file:=.obj) $(file:=.lib) $(file:=-cpp.exp) $(file:=-cpp.lib) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "all: \x24(TESTS)\r\n\r" >> $@
	@echo -e "clean:\r\n\tdel /Q \x24(CLEANFILES)\r\n\r" >> $@

	@$(foreach file,$(TESTS),echo -e "$(file:=.exe): $(file:=.c)\r\n\t\x24(CC) \x24(CFLAGS) /Fe\x24(@) \x24(?)\r\n$(file:=.cpp): $(file:=.c)\r\n\tcopy /Y \x24(?) \x24(@) >NUL\r\n$(file:=-cpp.exe): $(file:=.cpp)\r\n\t\x24(CXX) \x24(CXXFLAGS) /Fe\x24(@) \x24(?)\r\n\r" >> $@;)

Makefile.icl: Makefile
	@echo -e "# Generated by Makefile. Do not edit.\r" > $@
	@echo -e "CC = icl\r" >> $@
	@echo -e "CXX = \x24(CC)\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "CFLAGS = /nologo /Wall /wd4464 /wd4514\r" >> $@
	@echo -e "CXXFLAGS = \x24(CFLAGS)\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "TESTS = \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.exe) $(file:=-cpp.exe) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "TESTS = \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.exe) $(file:=-cpp.exe) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "CLEANFILES = \x24(TESTS) \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.cpp) $(file:=.obj) $(file:=.lib) $(file:=-cpp.exp) $(file:=-cpp.lib) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "all: \x24(TESTS)\r\n\r" >> $@
	@echo -e "clean:\r\n\tdel /Q \x24(CLEANFILES)\r\n\r" >> $@

	@$(foreach file,$(TESTS),echo -e "$(file:=.exe): $(file:=.c)\r\n\t\x24(CC) \x24(CFLAGS) /Fe\x24(@) \x24(?)\r\n$(file:=.cpp): $(file:=.c)\r\n\tcopy /Y \x24(?) \x24(@) >NUL\r\n$(file:=-cpp.exe): $(file:=.cpp)\r\n\t\x24(CXX) \x24(CXXFLAGS) /Fe\x24(@) \x24(?)\r\n\r" >> $@;)

Makefile.pelles: Makefile
	@echo -e "# Generated by Makefile. Do not edit.\r" > $@
	@echo -e "CC = pocc\r" >> $@
	@echo -e "LD = polink\r" >> $@
	@echo -e "\r" >> $@

	@echo -e "CFLAGS = \r" >> $@
	@echo -e "LDFLAGS = \r" >> $@
	@echo -e "\r" >> $@

	@echo -e "TESTS = \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.exe) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "CLEANFILES = \x24(TESTS) \x5c\r" >> $@
	@$(foreach file,$(TESTS),echo -e "\t$(file:=.obj) $(file:=.exe) \x5c\r" >> $@;)
	@echo -e "\t\x24(NULL)\r\n\r" >> $@

	@echo -e "all: \x24(TESTS)\r\n\r" >> $@
	@echo -e "clean:\r\n\tdel /Q \x24(CLEANFILES)\r\n\r" >> $@

	@$(foreach file,$(TESTS),echo -e "$(file:=.obj): $(file:=.c)\r\n\t\x24(CC) \x24(CFLAGS) $(file:=.c)\r\n\r\n$(file:=.exe): $(file:=.obj)\r\n\t\x24(LD) \x24(LDFLAGS) /OUT:$(file:=.exe) $(file:=.obj)\r\n\r" >> $@;)

clean:
	rm -f $(CLEANFILES)

.PHONY: clean all

ifeq ($(notdir $(CC)), gcc)
$(foreach tgt,unavailable warn unknown-pragmas,$(eval $(call GEN_GCC_C_WARNINGS,$(tgt))))
else ifeq ($(notdir $(CC)), x86_64-w64-mingw32-gcc)
$(foreach tgt,unavailable warn unknown-pragmas,$(eval $(call GEN_GCC_C_WARNINGS,$(tgt))))
endif

ifeq ($(notdir $(CXX)), g++)
$(foreach tgt,unavailable warn unknown-pragmas,$(eval $(call GEN_GCC_CXX_WARNINGS,$(tgt))))
else ifeq ($(notdir $(CXX)), x86_64-w64-mingw32-g++)
$(foreach tgt,unavailable warn unknown-pragmas,$(eval $(call GEN_GCC_CXX_WARNINGS,$(tgt))))
endif
