# libfirm Makefile
#
# This is currently experimental and not fully supported, but we plan to replace
# the jambuild with this at some point in the future.
#
# Most variable names are similar to the names used by autoconf...
-include config.mak

# Some build configuration defaults
top_srcdir   ?= .
top_builddir ?= build
variant      ?= debug

srcdir       ?= $(top_srcdir)
builddir     ?= $(top_builddir)/$(variant)
gendir       ?= $(top_builddir)/gen
docdir       ?= $(top_builddir)/doc

# This hides the noisy commandline outputs. Show them with "make V=1"
ifneq ($(V),1)
Q ?= @
endif

# Tools
CC ?= cc
DOXYGEN ?= doxygen
LINK ?= $(CC)
AR ?= ar
ifeq ("$(shell uname)", "Darwin")
DLLEXT ?= .dylib
LINKDLLFLAGS = -dynamiclib -install_name $(abspath $@)
else
DLLEXT ?= .so
LINKDLLFLAGS = -shared
endif

# Variants
CFLAGS_debug       = -O0 -g3
CFLAGS_profile     = -O3 -pg -fno-inline -DNDEBUG
CFLAGS_coverage    = -O0 --coverage
CFLAGS_optimize    = -O3 -fomit-frame-pointer -DNDEBUG
LINKFLAGS_debug    =
LINKFLAGS_profile  = -pg
LINKFLAGS_coverage = --coverage

ifeq ($(findstring -DNDEBUG, $(CFLAGS_$(variant))),)
CFLAGS += -DDEBUG_libfirm
endif

# General flags
CPPFLAGS  ?=
PICFLAG   ?= -fPIC
CFLAGS    += $(CFLAGS_$(variant)) -std=c99 $(PICFLAG) -DHAVE_FIRM_REVISION_H
CFLAGS    += -Wall -W -Wextra -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings
LINKFLAGS += $(LINKFLAGS_$(variant)) -lm
LINKFLAGS += $(if $(filter %cygwin %mingw32, $(shell $(CC) $(CFLAGS) -dumpmachine)), -lregex -lwinmm,)
VPATH = $(srcdir) $(gendir)

all: firm
.PHONY: all

# disable make builtin suffix rules
.SUFFIXES:

# libFirm
libfirm_SOURCES     = $(subst $(srcdir)/,,$(wildcard $(srcdir)/ir/*/*.c))
libfirm_GEN_SOURCES =
libfirm_DIRS        = $(sort $(dir $(libfirm_SOURCES))) include/libfirm include/libfirm/adt
libfirm_GEN_DIRS    = $(sort $(dir $(libfirm_GEN_SOURCES)))
libfirm_INCLUDEDIRS = $(addprefix $(srcdir)/, $(libfirm_DIRS)) $(addprefix $(gendir)/, $(libfirm_GEN_DIRS))
libfirm_a           = $(builddir)/libfirm.a
libfirm_dll         = $(builddir)/libfirm$(DLLEXT)
libfirm_CPPFLAGS    = $(foreach dir,$(libfirm_INCLUDEDIRS),-I$(dir))
libfirm_OBJECTS     = $(libfirm_SOURCES:%.c=$(builddir)/%.o) $(libfirm_GEN_SOURCES:%.c=$(builddir)/%.o)
libfirm_DEPS        = $(libfirm_OBJECTS:%.o=%.d)
libfirm_BUILDDIRS   = $(sort $(dir $(libfirm_OBJECTS))) $(addprefix $(gendir)/, $(libfirm_GEN_DIRS))

.PHONY: firm
firm: $(libfirm_dll) $(libfirm_a)

# backends
backends = amd64 arm ia32 mips riscv sparc TEMPLATE

EMITTER_GENERATOR = $(srcdir)/ir/be/scripts/generate_emitter.pl
REGALLOC_IF_GENERATOR = $(srcdir)/ir/be/scripts/generate_regalloc_if.pl
OPCODES_GENERATOR = $(srcdir)/ir/be/scripts/generate_new_opcodes.pl

define backend_template
$(1)_SOURCES = $$(subst $$(srcdir)/,,$$(wildcard $$(srcdir)/ir/be/$(1)/*.c))
$(1)_GEN_HEADERS =

$(1)_SPEC = ir/be/$(1)/$(1)_spec.pl

$$(gendir)/ir/be/$(1)/gen_$(1)_emitter.h $$(gendir)/ir/be/$(1)/gen_$(1)_emitter.c: $$($(1)_SPEC) $$(EMITTER_GENERATOR)
	@echo GEN $$@
	$(Q)$$(EMITTER_GENERATOR) ./$$< $$(gendir)/ir/be/$(1)
$(1)_GEN_SOURCES += ir/be/$(1)/gen_$(1)_emitter.c
$(1)_GEN_HEADERS += $$(gendir)/ir/be/$(1)/gen_$(1)_emitter.h

$$(gendir)/ir/be/$(1)/gen_$(1)_regalloc_if.h $$(gendir)/ir/be/$(1)/gen_$(1)_regalloc_if.c: $$($(1)_SPEC) $$(REGALLOC_IF_GENERATOR)
	@echo GEN $$@
	$(Q)$$(REGALLOC_IF_GENERATOR) ./$$< $$(gendir)/ir/be/$(1)
$(1)_GEN_SOURCES += ir/be/$(1)/gen_$(1)_regalloc_if.c
$(1)_GEN_HEADERS += $$(gendir)/ir/be/$(1)/gen_$(1)_regalloc_if.h

$$(gendir)/ir/be/$(1)/gen_$(1)_new_nodes.h $$(gendir)/ir/be/$(1)/gen_$(1)_new_nodes.c: $$($(1)_SPEC) $$(OPCODES_GENERATOR)
	@echo GEN $$@
	$(Q)$$(OPCODES_GENERATOR) ./$$< $$(gendir)/ir/be/$(1)
$(1)_GEN_SOURCES += ir/be/$(1)/gen_$(1)_new_nodes.c
$(1)_GEN_HEADERS += $$(gendir)/ir/be/$(1)/gen_$(1)_new_nodes.h

# We need to inform make of the headers it doesn't know yet...
$(1)_OBJECTS = $$($(1)_SOURCES:%.c=$$(builddir)/%.o) $$($(1)_GEN_SOURCES:%.c=$$(builddir)/%.o)
$$($(1)_OBJECTS): $$($(1)_GEN_HEADERS)

libfirm_GEN_SOURCES += $$($(1)_GEN_SOURCES)
libfirm_SOURCES += $$($1_SOURCES)
endef

$(foreach backend,$(backends),$(eval $(call backend_template,$(backend))))

# generators
IR_SPEC_GENERATED_INCLUDES := \
	$(gendir)/include/libfirm/nodes.h \
	$(gendir)/ir/ir/gen_proj_names.h  \
	$(gendir)/ir/ir/gen_irnode.h
IR_SPEC_GENERATOR := $(srcdir)/scripts/gen_ir.py
IR_SPEC_GENERATOR_DEPS := $(IR_SPEC_GENERATOR) $(srcdir)/scripts/jinjautil.py $(srcdir)/scripts/irops.py $(srcdir)/scripts/filters.py
IR_SPEC := $(srcdir)/scripts/ir_spec.py
libfirm_BUILDDIRS += $(gendir)/include/libfirm

libfirm_GEN_SOURCES += \
	ir/ir/gen_irnode.c \
	ir/ir/gen_irio.c
$(builddir)/ir/ir/gen_irnode.o: $(gendir)/ir/ir/gen_irnode.c
$(builddir)/ir/ir/gen_irio.o: $(gendir)/ir/ir/gen_irio.c

$(gendir)/ir/ir/% : scripts/templates/% $(IR_SPEC_GENERATOR_DEPS) $(IR_SPEC)
	@echo GEN $@
	$(Q)$(IR_SPEC_GENERATOR) $(IR_SPEC) "$<" > "$@"

$(gendir)/include/libfirm/% : scripts/templates/% $(IR_SPEC_GENERATOR_DEPS) $(IR_SPEC)
	@echo GEN $@
	$(Q)$(IR_SPEC_GENERATOR) $(IR_SPEC) "$<" > "$@"

libfirm_GEN_DIRS += ir/ir include/libfirm

$(libfirm_a): $(libfirm_OBJECTS)
	@echo AR $@
	$(Q)rm -f $@
	$(Q)$(AR) -crs $@ $^

$(libfirm_dll): $(libfirm_OBJECTS)
	@echo LINK $@
	$(Q)$(LINK) $(LINKDLLFLAGS) $^ -o $@ $(LINKFLAGS)

# Determine if we can use cparser-beta for quickcheck
QUICKCHECK_DEFAULT := $(shell which cparser-beta 2> /dev/null || echo true) -fsyntax-only
QUICKCHECK ?= $(QUICKCHECK_DEFAULT)
QUICKCHECK_FLAGS ?= -m32 -Wno-compat-option -Wno-shadow -Wno-shadow-local -Wunreachable-code

$(builddir)/%.o: %.c $(IR_SPEC_GENERATED_INCLUDES)
	@echo CC $@
	$(Q)$(QUICKCHECK) $(QUICKCHECK_FLAGS) $(CFLAGS) $(CPPFLAGS) $(libfirm_CPPFLAGS) $(QUICKCHECK_FLAGS) $<
	$(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(libfirm_CPPFLAGS) -MP -MMD -c -o $@ $<

$(docdir)/libfirm.tag: doc/Doxyfile doc/logo.png $(IR_SPEC_GENERATED_INCLUDES) $(wildcard include/libfirm/*.h) $(wildcard include/libfirm/adt/*.h)
	@echo Doxygen $@
	$(Q)$(DOXYGEN) $<

.PHONY: doc
doc: $(docdir)/libfirm.tag

.PHONY: clean
clean:
	@echo CLEAN
	$(Q)rm -fr $(builddir) $(gendir) $(docdir)

.PHONY: install
PREFIX ?= /usr/local
INSTALL ?= install
INSTALLPREFIX = $(DESTDIR)$(PREFIX)
install: $(libfirm_a) $(libfirm_dll)
	$(INSTALL) -d "$(INSTALLPREFIX)/include/libfirm"
	$(INSTALL) -m0644 include/libfirm/*.h "$(INSTALLPREFIX)/include/libfirm"
	$(INSTALL) -m0644 "$(gendir)"/include/libfirm/*.h "$(INSTALLPREFIX)/include/libfirm"
	$(INSTALL) -d "$(INSTALLPREFIX)/include/libfirm/adt"
	$(INSTALL) -m0644 include/libfirm/adt/*.h "$(INSTALLPREFIX)/include/libfirm/adt"
	$(INSTALL) -d "$(INSTALLPREFIX)/lib"
	$(INSTALL) -m0644 $^ "$(INSTALLPREFIX)/lib"

# Ensure all output directories are created
UNUSED1 := $(shell mkdir -p $(libfirm_BUILDDIRS))

REVISION ?= $(shell git --git-dir $(top_srcdir)/.git describe --abbrev=40 --always --dirty --match '')

# Update revision.h if necessary
REVISIONH = $(gendir)/firm_revision.h
libfirm_INCLUDEDIRS += $(gendir)
HASH := \#
UNUSED2 := $(shell \
	REV="$(HASH)define libfirm_VERSION_REVISION \"$(REVISION)\""; \
	echo "$$REV" | cmp -s - "$(REVISIONH)" 2> /dev/null || echo "$$REV" > "$(REVISIONH)" \
)

# Unit tests
UNITTESTS_SOURCES = $(subst $(srcdir)/unittests/,,$(wildcard $(srcdir)/unittests/*.c))
UNITTESTS         = $(UNITTESTS_SOURCES:%.c=$(builddir)/%.exe)
UNITTESTS_OK      = $(UNITTESTS_SOURCES:%.c=$(builddir)/%.ok)

$(builddir)/%.exe: $(srcdir)/unittests/%.c $(libfirm_a)
	@echo LINK $<
	$(Q)$(LINK) $(CFLAGS) $(CPPFLAGS) $(libfirm_CPPFLAGS) "$<" $(libfirm_a) -lm -o "$@"

$(builddir)/%.ok: $(builddir)/%.exe
	@echo EXEC $<
	$(Q)$< && touch "$@"

.PRECIOUS: $(UNITTESTS)
.PHONY: test
test: $(UNITTESTS_OK)

.PHONY: gen
gen: $(IR_SPEC_GENERATED_INCLUDES) $(libfirm_GEN_SOURCES)

-include $(libfirm_DEPS)
