# Makefile for the Localize.Mf2.TreeSitter NIF.
#
# Three translation units link into one shared library:
#
#   * c_src/runtime/src/lib.c          — tree-sitter runtime (single-TU amalgamation).
#   * c_src/grammar/parser.c           — tree-sitter-mf2 generated parser.
#   * c_src/ts_nif.c                   — erl_nif glue code.
#
# The runtime and grammar have different internal layouts, so each
# compilation step has its own -I path. They never share headers at
# compile time and are linked together into priv/localize_mf2_treesitter_nif.so.

ERL ?= erl
BASEDIR = $(CURDIR)
PRIVDIR = ./priv

NIF_DIR = c_src
NIF_ENV = c_src/env.mk

RUNTIME_DIR = $(NIF_DIR)/runtime
GRAMMAR_DIR = $(NIF_DIR)/grammar

RUNTIME_OBJ = $(RUNTIME_DIR)/lib.o
GRAMMAR_OBJ = $(GRAMMAR_DIR)/parser.o
NIF_OBJ     = $(NIF_DIR)/ts_nif.o
ALL_OBJS    = $(RUNTIME_OBJ) $(GRAMMAR_OBJ) $(NIF_OBJ)

NIF_SO = $(PRIVDIR)/localize_mf2_treesitter_nif.so

UNAME_SYS := $(shell uname -s)
UNAME_ARCH := $(shell uname -m)

CC ?= cc

# Optimisation + ISO C — tree-sitter requires C11.
CFLAGS_BASE = -O3 -std=c11 -Wall -Wextra -fPIC

ifeq ($(UNAME_SYS), Darwin)
  LDFLAGS = -arch $(UNAME_ARCH) -flat_namespace -undefined dynamic_lookup -shared
  CFLAGS_BASE += -arch $(UNAME_ARCH)
else
  LDFLAGS = -shared
endif

# Erlang NIF headers — resolved by the env.mk target below.
ERL_CFLAGS = -I$(ERTS_INCLUDE_DIR) -I$(ERL_INTERFACE_INCLUDE_DIR)

# Include paths, per translation unit.
RUNTIME_CFLAGS = -I$(RUNTIME_DIR)/include -I$(RUNTIME_DIR)/src

# tree-sitter's generated parser.c uses a single START_LEXER() macro
# that declares `int32_t lookahead` and a `next_state:` label. Both
# are by-design generic and some generated code paths legitimately
# don't consume them, so GCC/Clang emit `-Wunused-but-set-variable`
# and `-Wunused-label`. These are inherent to the generator output,
# not a sign of a bug in our build — suppress them for this one TU
# rather than loosening warnings globally.
GRAMMAR_CFLAGS = -I$(GRAMMAR_DIR) -Wno-unused-but-set-variable -Wno-unused-label

NIF_CFLAGS     = -I$(RUNTIME_DIR)/include $(ERL_CFLAGS)

all: $(NIF_ENV) $(NIF_SO)

clean:
	@rm -f $(ALL_OBJS) $(NIF_SO)

distclean: clean
	@rm -f $(NIF_ENV)

$(NIF_SO): $(ALL_OBJS)
	@mkdir -p $(PRIVDIR)
	$(CC) $(ALL_OBJS) $(LDFLAGS) -o $(NIF_SO)

$(RUNTIME_OBJ): $(RUNTIME_DIR)/src/lib.c
	$(CC) -c $(CFLAGS_BASE) $(RUNTIME_CFLAGS) $< -o $@

$(GRAMMAR_OBJ): $(GRAMMAR_DIR)/parser.c
	$(CC) -c $(CFLAGS_BASE) $(GRAMMAR_CFLAGS) $< -o $@

$(NIF_OBJ): $(NIF_DIR)/ts_nif.c
	$(CC) -c $(CFLAGS_BASE) $(NIF_CFLAGS) $< -o $@

$(NIF_ENV):
	@$(ERL) -eval "file:write_file(\"$(NIF_ENV)\", \
		io_lib:format( \
		\"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \
		\"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \
		\"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \
		[code:root_dir(), erlang:system_info(version), \
		code:lib_dir(erl_interface, include), \
		code:lib_dir(erl_interface, lib)])), \
		halt()."

-include $(NIF_ENV)
