# Makefile for regexx: a single-file C regex interpreter with Python
# `re` semantics. See concept.md for the design and README.md for the
# implementation status.

CC       ?= cc
CSTD     ?= -std=c11
WARN     ?= -Wall -Wextra
OPT      ?= -O2
CFLAGS   ?= $(CSTD) $(WARN) $(OPT) -g
LDLIBS   ?=

PREFIX   ?= /usr/local

AR       ?= ar

.PHONY: all lib example examples test check clean install fuzz-smoke

all: lib example

# ---- library -----------------------------------------------------------
libregexx.a: regexx.o
	$(AR) rcs $@ $^

regexx.o: regexx.c regexx.h
	$(CC) $(CFLAGS) -c regexx.c -o $@

lib: libregexx.a

# ---- example -------------------------------------------------------------
example: rxgrep

rxgrep: examples/rxgrep.c libregexx.a regexx.h
	$(CC) $(CFLAGS) -I. -o $@ examples/rxgrep.c libregexx.a $(LDLIBS)

# ---- feature examples ------------------------------------------------------
# Each program under examples/ (besides rxgrep) isolates one distinct,
# less-obvious feature (a data mode, an empty-match rule, a ReDoS
# mitigation, mmap-backed large file input) rather than being a general
# purpose tool; see examples/README.md for what each one demonstrates.
EXAMPLE_BINS = binary_scan utf8_scripts ascii_logparse redos_atomic empty_match_rule large_file_search bench_vs_posix

examples: rxgrep $(EXAMPLE_BINS)

binary_scan: examples/binary_scan.c libregexx.a regexx.h
	$(CC) $(CFLAGS) -I. -o $@ examples/binary_scan.c libregexx.a $(LDLIBS)

utf8_scripts: examples/utf8_scripts.c libregexx.a regexx.h
	$(CC) $(CFLAGS) -I. -o $@ examples/utf8_scripts.c libregexx.a $(LDLIBS)

ascii_logparse: examples/ascii_logparse.c libregexx.a regexx.h
	$(CC) $(CFLAGS) -I. -o $@ examples/ascii_logparse.c libregexx.a $(LDLIBS)

redos_atomic: examples/redos_atomic.c libregexx.a regexx.h
	$(CC) $(CFLAGS) -I. -o $@ examples/redos_atomic.c libregexx.a $(LDLIBS)

empty_match_rule: examples/empty_match_rule.c libregexx.a regexx.h
	$(CC) $(CFLAGS) -I. -o $@ examples/empty_match_rule.c libregexx.a $(LDLIBS)

large_file_search: examples/large_file_search.c libregexx.a regexx.h
	$(CC) $(CFLAGS) -I. -o $@ examples/large_file_search.c libregexx.a $(LDLIBS)

bench_vs_posix: examples/bench_vs_posix.c libregexx.a regexx.h
	$(CC) $(CFLAGS) -I. -o $@ examples/bench_vs_posix.c libregexx.a $(LDLIBS)

# ---- tests -----------------------------------------------------------------
# tests/generated_tests.c is generated from tests/cases.py using
# CPython's own `re` module as ground truth (concept.md Section 11).
tests/generated_tests.c: tests/cases.py tests/gen.py
	python3 tests/gen.py

test: tests/generated_tests.c regexx.c regexx.h tests/harness.c tests/harness.h tests/main.c
	$(CC) $(CFLAGS) -I. -o /tmp/regexx_test regexx.c tests/harness.c tests/generated_tests.c tests/main.c $(LDLIBS)
	/tmp/regexx_test

# check also runs the suite under AddressSanitizer + UBSan.
check: tests/generated_tests.c
	$(CC) $(CSTD) $(WARN) -O0 -g -fsanitize=address,undefined -I. -o /tmp/regexx_test_san \
		regexx.c tests/harness.c tests/generated_tests.c tests/main.c $(LDLIBS)
	/tmp/regexx_test_san

install: libregexx.a regexx.h
	install -d $(DESTDIR)$(PREFIX)/lib $(DESTDIR)$(PREFIX)/include
	install -m644 libregexx.a $(DESTDIR)$(PREFIX)/lib/
	install -m644 regexx.h $(DESTDIR)$(PREFIX)/include/

clean:
	rm -f regexx.o libregexx.a rxgrep tests/generated_tests.c $(EXAMPLE_BINS)
