# Semana 1: de C a C++. Se usa dentro del entorno del curso.
#
#   make           compila los programas de la clase en build/
#   make measure   compila con -O2 y mide tiempos (tarda cerca de medio minuto)
#   make lab       compila el laboratorio en build/lab/
#   make test      corre las pruebas de los ejemplos y de los apuntes
#   make test-lab  prueba su laboratorio; falla hasta que lo terminen
#   make compare   cuenta líneas y memoria a mano de las dos versiones (paso 6)
#   make clean     borra lo generado
#
# Los programas de errors/ tienen errores a propósito. No se compilan con make:
# los compilan las pruebas, y ustedes, con los comandos de los apuntes.

CC = gcc
CXX = g++
CFLAGS = -Wall -Wextra -std=c17 -g -fsanitize=address,undefined
CXXFLAGS = -Wall -Wextra -std=c++20 -g -fsanitize=address,undefined
FASTFLAGS = -Wall -Wextra -std=c++20 -O2
BUILD = build
LAB_DIR ?= lab
PROGRAMS = hello c_style small_changes namespaces reference_basics swap_both const_forms \
           copy_or_not const_shallow course strings vectors range_for lookup
C_MODULE = lab/c/grade_book.c lab/c/grade_book.h
CPP_MODULE = $(LAB_DIR)/grade_book.cpp $(LAB_DIR)/grade_book.h

.PHONY: all lab measure test test-lab compare clean

all: $(PROGRAMS:%=$(BUILD)/%) $(BUILD)/copy_in_loop $(BUILD)/sizes \
     $(BUILD)/tally_demo $(BUILD)/tally_tests $(BUILD)/tally_c_demo

$(BUILD)/%: %.cpp | $(BUILD)
	$(CXX) $(CXXFLAGS) $< -o $@

$(BUILD)/copy_in_loop: copy_in_loop.cpp value_at.cpp value_at.h | $(BUILD)
	$(CXX) $(CXXFLAGS) copy_in_loop.cpp value_at.cpp -o $@

$(BUILD)/sizes: sizes.cpp tally.cpp tally.h | $(BUILD)
	$(CXX) $(CXXFLAGS) sizes.cpp tally.cpp -o $@

$(BUILD)/tally_demo: tally_demo.cpp tally.cpp tally.h | $(BUILD)
	$(CXX) $(CXXFLAGS) tally_demo.cpp tally.cpp -o $@

$(BUILD)/tally_tests: tally_tests.cpp tally.cpp tally.h | $(BUILD)
	$(CXX) $(CXXFLAGS) tally_tests.cpp tally.cpp -o $@

$(BUILD)/tally_c_demo: tally_c/tally_demo.c tally_c/tally.c tally_c/tally.h | $(BUILD)
	$(CC) $(CFLAGS) tally_c/tally_demo.c tally_c/tally.c -o $@

# Para medir: -O2 y sin sanitizers.
$(BUILD)/fast/lookup: lookup.cpp | $(BUILD)/fast
	$(CXX) $(FASTFLAGS) $< -o $@

$(BUILD)/fast/copy_in_loop: copy_in_loop.cpp value_at.cpp value_at.h | $(BUILD)/fast
	$(CXX) $(FASTFLAGS) copy_in_loop.cpp value_at.cpp -o $@

measure: $(BUILD)/fast/lookup $(BUILD)/fast/copy_in_loop
	python3 measure.py

# Laboratorio: el módulo en C que se les da, y la clase que escriben.
$(BUILD)/lab/hello: lab/hello.cpp | $(BUILD)/lab
	$(CXX) $(CXXFLAGS) $< -o $@

$(BUILD)/lab/test_grade_book_c: lab/c/test_grade_book.c $(C_MODULE) | $(BUILD)/lab
	$(CC) $(CFLAGS) lab/c/test_grade_book.c lab/c/grade_book.c -o $@

$(BUILD)/lab/driver_c: lab/c/driver.c $(C_MODULE) | $(BUILD)/lab
	$(CC) $(CFLAGS) lab/c/driver.c lab/c/grade_book.c -o $@

$(BUILD)/lab/test_grade_book: $(LAB_DIR)/test_grade_book.cpp $(CPP_MODULE) | $(BUILD)/lab
	$(CXX) $(CXXFLAGS) -I$(LAB_DIR) $(LAB_DIR)/test_grade_book.cpp $(LAB_DIR)/grade_book.cpp -o $@

$(BUILD)/lab/public_port: lab/tests/public_port.cpp $(CPP_MODULE) | $(BUILD)/lab
	$(CXX) $(CXXFLAGS) -I$(LAB_DIR) lab/tests/public_port.cpp $(LAB_DIR)/grade_book.cpp -o $@

$(BUILD)/lab/driver: lab/tests/driver.cpp $(CPP_MODULE) | $(BUILD)/lab
	$(CXX) $(CXXFLAGS) -I$(LAB_DIR) lab/tests/driver.cpp $(LAB_DIR)/grade_book.cpp -o $@

$(BUILD) $(BUILD)/fast $(BUILD)/lab:
	mkdir -p $@

lab: $(BUILD)/lab/hello $(BUILD)/lab/test_grade_book_c $(BUILD)/lab/driver_c \
     $(BUILD)/lab/test_grade_book $(BUILD)/lab/public_port $(BUILD)/lab/driver

# Si existe solution/, test_lab.py también comprueba la solución del laboratorio.
test: all
	LAB_DIR=solution python3 -m unittest discover -p 'test_*.py'

test-lab: all
	LAB_DIR=$(LAB_DIR) python3 -m unittest -v test_lab

compare:
	@echo "módulo en C:"
	@wc -l lab/c/grade_book.h lab/c/grade_book.c
	@echo "clase en C++ ($(LAB_DIR)):"
	@wc -l $(LAB_DIR)/grade_book.h $(LAB_DIR)/grade_book.cpp
	@echo "llamadas a malloc, realloc y free en C:"
	@cat lab/c/grade_book.c | sed 's://.*::' | grep -E -c '\b(malloc|realloc|free)\(' || true
	@echo "llamadas a malloc, realloc y free en C++:"
	@cat $(LAB_DIR)/grade_book.cpp | sed 's://.*::' | grep -E -c '\b(malloc|realloc|free)\(' || true

clean:
	rm -rf $(BUILD) __pycache__
